您正在寻找的规则在 §3.10/10 中(在 C++11 中):
如果程序尝试通过非下列类型之一的泛左值访问对象的存储值,则行为未定义: — 对象的动态类型,
— 对象动态类型的 cv 限定版本,
— 与对象的动态类型类似(如 4.4 中定义)的类型,
— 对应于对象动态类型的有符号或无符号类型, — 对应于对象动态类型的 cv 限定版本的有符号或无符号类型,
— 在其元素或非静态数据成员中包含上述类型之一的聚合或联合类型(递归地,包括子聚合或包含联合的元素或非静态数据成员),
— 作为对象动态类型的(可能是 cv 限定的)基类类型的类型,
— char 或 unsignedchar 类型。
未定义的行为有不同的类型(或动机)。
在强制转换int*
到float*
然后取消引用它的情况下,很明显标准无法定义它,因为可能发生的情况取决于架构和int
. 另一方面,引用的段落是完全错误的——使用memcpy
来进行转换也是未定义的行为,原因大致相同。
未定义行为的动机之一是允许实现以对目标架构有意义的方式定义它,如果存在的话。这是这样一种情况。故意导致它失败的编译器是有缺陷的。当然,如果我们假设 32 位 2 的补码
int
和 32 位 IEEE float
,我们可能会期望 的某些值int
对应于捕获 NaN,这将导致程序失败。这是行为未定义的部分原因;允许这样的事情发生。但是如果我们熟悉硬件的底层细节,它应该可以按预期工作,前提是编译器可以看到演员表。如果没有,这是编译器的 QoI 问题,对于此类工作应避免使用此类编译器。
正如上面所暗示的,这种特殊情况,事实上,在所有涉及类型双关语的情况下(例如,写入联合的一个成员,并从另一个成员读取)确实会造成问题,标准尚未解决找到适当的措辞。出现问题是因为通常允许编译器假定指向不同类型(字符类型除外)的指针没有别名;aint*
永远不能和 a 指向同一个对象float*
. 并且证明两个指针不能别名对于优化很重要。即使标准说它是未定义的行为,在指针强制转换或联合清晰可见的地方破坏代码的编译器也会被破坏。一个编译器在它看到的只是两个指向不相关类型的指针的情况下中断代码是可以理解的,即使在标准规定行为定义良好的情况下也是如此。
Usingmemcpy
通过使用两个不同的对象来避免这个问题,这两个对象没有别名。它仍然会遇到未定义的行为,因为将 a 的位模式int
放入 a float
,然后访问浮点数,没有任何已定义的行为。(反之亦然;我知道至少有一台机器将 a 的位复制float
到 anint
可能会导致非法int
值。)