我有gcc优化错误或C代码问题吗?

测试以下代码:

#include  #include  main() { const char *yytext="0"; const float f=(float)atof(yytext); size_t t = *((size_t*)&f); printf("t should be 0 but is %d\n", t); } 

编译它:

 gcc -O3 test.c 

GOOD输出应该是:

 "t should be 0 but is 0" 

但是使用我的gcc 4.1.3,我有:

 "t should be 0 but is -1209357172" 

使用编译器标志-fno-strict-aliasing。

启用严格别名,因为默认情况下至少为-O3,在行中:

 size_t t = *((size_t*)&f); 

编译器假定size_t *不指向与float *相同的内存区域。 据我所知,这是符合标准的行为(遵循ANSI标准中的严格别名规则开始围绕gcc-4,正如Thomas Kammeyer所指出的那样)。

如果我没记错的话,你可以使用一个中间强制转换为char *来解决这个问题。 (编译器假定char *可以别名)

换句话说,尝试这个(现在不能自己测试,但我认为它会起作用):

 size_t t = *((size_t*)(char*)&f); 

在C99标准中,6.5-7中的以下规则涵盖了这一点:

对象的存储值只能由具有以下类型之一的左值表达式访问:73)

  • 与对象的有效类型兼容的类型,

  • 与对象的有效类型兼容的类型的限定版本,

  • 与对象的有效类型对应的有符号或无符号类型的类型,

  • 与有效类型的对象的限定版本对应的有符号或无符号类型的类型,

  • 在其成员中包含上述类型之一的聚合或联合类型(包括递归地,子聚合或包含联合的成员),或者

  • 一个字符类型。

最后一项是为什么先铸造(char *)的原因。

根据指针别名的C99规则,不再允许这样做。 两种不同类型的指针不能指向内存中的相同位置。 此规则的例外是void和char指针。

因此,在您转换为size_t指针的代码中,编译器可以选择忽略它。 如果你想将float值作为size_t得到,只需指定它,浮动将被转换(截断而不是舍入),如下所示:

size_t size =(size_t)(f); //这个有效

这通常被报告为一个错误,但事实上它确实是一个允许优化器更有效地工作的function。

在gcc中,您可以使用编译器开关禁用它。 我相信-fno_strict_aliasing。

这是不好的C代码:-)

有问题的部分是你通过将一个对象转换为一个整数指针并取消引用它来访问一个float类型的对象。

这会破坏别名规则。 编译器可以自由地假设指向不同类型的指针(如float或int)在内存中不重叠。 你完全是这样做的。

编译器看到的是你计算的东西,将它存储在浮点数f中并且永远不再访问它。 很可能编译器已经删除了部分代码,并且从未发生过赋值。

通过size_t指针解除引用将在这种情况下从堆栈返回一些未初始化的垃圾。

你可以做两件事来解决这个问题:

  1. 使用带有float和size_t成员的union,并通过类型punning进行转换。 不好但是很有效。

  2. 使用memcopy将f的内容复制到size_t中。 编译器足够智能,可以检测和优化这种情况。

为什么你认为t应该是0?

或者,准确地说,“为什么你会认为浮点零的二进制表示与整数零的二进制表示相同?”

这是糟糕的C代码。 您的强制转换会破坏C别名规则,并且优化器可以自由地执行破坏此代码的操作。 您可能会发现GCC已经在浮点写入之前调整了size_t读取(以隐藏fp管道延迟)。

您可以设置-fno-strict-aliasing开关,或使用union或reinterpret_cast以符合标准的方式重新解释该值。

除了指针对齐,你期望sizeof(size_t)== sizeof(float)。 我不认为它(在64位Linux size_t应该是64位但浮动32位),这意味着你的代码将读取未初始化的东西。

-O3不被视为“理智”,-O2通常是上限,除了某些多媒体应用程序。

有些应用程序甚至无法走得那么远,如果你超越-O1就会死掉。

如果你有一个足够新的GCC(我在4.3这里),它可能支持这个命令

  gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts 

如果你小心,你可能会通过该列表找到你正在启用的给定的单一优化导致这个错误。

来自man gcc

  The output is sensitive to the effects of previous command line options, so for example it is possible to find out which optimizations are enabled at -O2 by using: -O2 --help=optimizers Alternatively you can discover which binary optimizations are enabled by -O3 by using: gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts diff /tmp/O2-opts /tmp/O3-opts | grep enabled 

我测试了你的代码:“i686-apple-darwin9-gcc-4.0.1(GCC)4.0.1(Apple Inc. build 5465)”

而且没有问题。 输出:

 t should be 0 but is 0 

所以你的代码中没有错误。 这并不意味着它是好的代码。 但我会添加主函数的返回类型和“返回0”; 在function的最后。