在C中,读取是否超过了对象未定义的行为?

在C中,读取是否超过了对象未定义的行为?,c,undefined-behavior,C,Undefined Behavior,将整数地址转换为双指针并读取,但整数的大小小于double type,读取操作将读取超过对象大小。我相信这是未定义的行为,但我没有在C标准中找到描述,所以我发布这个问题以寻求答案来确认我的观点 #include <stdio.h> #include <stdint.h> int main() { int32_t a = 12; double *p = (double*)(&a); printf("%lf\n", *p); retu

将整数地址转换为双指针并读取,但整数的大小小于double type,读取操作将读取超过对象大小。我相信这是未定义的行为,但我没有在C标准中找到描述,所以我发布这个问题以寻求答案来确认我的观点

#include <stdio.h>
#include <stdint.h>

int main() {
    int32_t a = 12;
    double *p = (double*)(&a);
    printf("%lf\n", *p);
    return 0;
}
#包括
#包括
int main(){
int32_t a=12;
双*p=(双*)(&a);
printf(“%lf\n”,*p);
返回0;
}
来自
6.5表达式
第7点:

对象的存储值只能由具有以下类型之一的左值表达式访问:76)
-与对象的有效类型兼容的类型,
-与对象的有效类型兼容的类型的限定版本,
-与对象的有效类型相对应的有符号或无符号类型,
-一种类型,它是与对象的有效类型的限定版本相对应的有符号或无符号类型,
-在其成员中包含上述类型之一的聚合或联合类型(递归地包括子聚合或包含的联合的成员),或
-字符类型


int
类型的对象使用
double
类型的左值表达式访问其地址
int
double
类型无论如何都不兼容,它们不是聚合的,而且
double
不是字符类型。取消引用指向int类型对象的double类型的指针(左值表达式)是未定义的行为。此类操作称为严格别名冲突。

根据C116.5(“严格别名规则”)这是未定义的行为:

6访问其存储值的对象的有效类型是该对象的声明类型(如果有)。

在这种情况下,有效类型是
int32\u t
(这是一个与
int
long
类似的类型相对应的typedef)

7对象的存储值只能由具有以下类型之一的左值表达式访问:
-与对象的有效类型兼容的类型,

double
int32\u t
不兼容,因此当代码访问此处的数据时:
*p
,它违反此规则并调用UB


有关详细信息,请参阅。

如果使用与“int”类型没有可见关系的左值访问类型为“int”的对象,则该标准不要求编译器的行为可预测。然而,在基本原理中,作者指出,将某些行为分类为未定义的行为是为了让市场决定在质量实施中哪些行为是必要的。一般来说,将指针转换为另一种类型,然后立即使用它执行访问的操作属于高质量编译器支持的操作类别,这些编译器被配置为适合系统编程,但可能不受行为迟钝的编译器的支持

然而,即使忽略了左值类型的问题,该标准也没有对应用程序试图从它不拥有的内存中读取时会发生什么提出任何要求。在这里,行为的选择有时可能是实现质量的问题。这里有五种主要的可能性:

  • 在某些实现中,存储的内容可能是 可预测通过标准未描述的方式,以及 将产生此类存储的内容

  • 阅读的行为可能会表现得好像它产生了带有 未指定的值,但没有其他副作用

  • 尝试读取可能会终止程序

  • 在使用内存映射I/O的平台上,越界读取可能会失败 执行后果未知的意外操作;这 可能性仅适用于某些平台

  • 以各种方式尝试“聪明”的实现可能会尝试 根据读取不可能发生的概念得出推论,因此 产生超越时间和因果关系法则的副作用

  • 如果您知道您的代码将运行在具有读取功能的平台上 没有副作用,实现不会试图变得“聪明”,您的代码 准备好读取可能产生的任何位模式,然后在这些模式下 在这种情况下,阅读可能会有一些有用的行为,但你会受到限制 可以使用代码的情况

    请注意,虽然需要定义
    \uuuuu STDC\u ANALYZABLE\uuuuu
    的实现 让大多数行为遵守时间法则和因果关系,即使在 该标准不会提出其他要求,超出范围的读数为 被归类为关键的未定义行为,因此应予以考虑 对未明确规定的任何实现都是危险的

    顺便说一句,即使代码使用了
    int[3]
    而不是单个
    int
    ,在某些平台上也会出现另一个问题:对齐。在某些平台上,某些类型的值只能读写到某些地址,而某些适合较小类型的地址可能不适合较大类型的地址。在
    int
    需要32位对齐而
    double
    需要64位对齐的平台上,给定
    int-foo[3]
    ,编译器可以任意放置
    foo
    ,以便
    (double*)foo
    将是存储
    double
    的合适地址,或者
    (double*)(foo+1)
    将是一个合适的地方。熟悉实现细节的程序员可能能够确定哪个地址是有效的,并利用它,但是盲目假设
    foo
    的地址是有效的代码可能会