我知道违反严格别名规则是根据C标准的未定义行为。请不要告诉我这是UB,没有什么可谈的。
我想知道是否有编译器对以下代码没有预期的行为(由我在下面定义)。
假设float
和int
的大小为4个字节,并且是一个大端机器。
float f = 1234.567; /* Any value here */
unsigned int u = *(unsigned int *)&f;
我在英语单词中的预期行为是"获取存储float
的四个字节,并将它们放在int
中" 。在代码中它将是这个(我认为这里没有UB):
float f = 1234.567; /* Any value here */
unsigned char *p = (unsigned char *)&f;
unsigned int u = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
我也欢迎实际和具体的例子,为什么除了符合标准的UB之外,编译器会有我认为是意外的行为。
答案 0 :(得分:8)
在大多数编译器上,它会按照您的期望执行,直到优化程序决定使用死代码消除或将赋值移动到f 。
这使得基本上不可能测试任何给定的编译器是否总是总是做你期望的 - 它可能适用于一个特定的程序,但稍微不同的可能会失败。严格别名规则基本上只是告诉编译器实现者&#34;你可以通过假设它们从不别名来自由地重新排列和消除这些东西。如果执行导致此代码失败的操作无效,优化程序可能会赢,但您不会发现问题。
最重要的是,它不是有用的来谈论&#34;这些编译器将会工作在&#34;,因为它可能突然停止在将来的任何一个上工作一些看似无关的变化。
答案 1 :(得分:1)
float f = 1234.567; /* Any value here */
unsigned int u = *(unsigned int *)&f;
为什么这不能按预期工作的一些合理的原因是:
float
和unsigned int
的大小不同。 (我已经研究过int
为64位且float
为32位的系统。我还研究过int
和float
都是64位的系统,所以你假设复制4个字节会失败。)
float
和unsigned int
具有不同的对齐要求。具体来说,如果unsigned int
需要比float
更严格的对齐,并且f
恰好是严格对齐的,那么将f
视为unsigned int
就好了的东西。 (如果int
和float
的大小相同,则可能。
编译器可能会识别出代码的行为未定义,例如优化掉分配。 (我没有具体的例子。)
如果您想将float
的表示复制到unsigned int
,memcpy()
更安全(我首先会检查它们实际上是否具有相同的大小)。如果要检查float
对象的表示,那么执行该操作的规范方法是将其复制到unsigned char
的数组中。引用ISO C标准(N1570草案中的6.2.6.1p4):
存储在任何其他对象类型的非位字段对象中的值 由 n ×
CHAR_BIT
位组成,其中 n 是一个大小 该类型的对象,以字节为单位。该值可以复制到对象中 类型unsigned char [
n]
(例如,memcpy
);该 结果字节集称为对象表示 值。
答案 2 :(得分:0)
您无缘无故地调用未定义的行为。
这种严格别名规则违规是否会产生我期望的行为?
没有。而且你不需要任何期望,因为你可以编写更好看的代码。
这定义了您喜欢的行为:
union {
float f;
uint32_t i;
} ufi_t;
assert(sizeof(float) == sizeof(uint32_t);
ufi_t u = { 123.456 };
uint32_t i = u.i;
你可以把它分解出去,好的编译器不会为它生成代码:
inline uint32_t int_from_float(float f) {
ufi_t u = { f };
return u.i;
}
您也可以安全地从(* float)转换为(* ufi_t)。所以:
float f = 123.456;
uint32_t i = ((ufi_t*)&f)->i;
注意:欢迎语言律师直截了当地说明这一点,但这是我对C9899:201x 6.5的看法等等。