C++ gcc优化?缺陷及其对工程实践的启示

C++ gcc优化?缺陷及其对工程实践的启示,c++,optimization,gcc,C++,Optimization,Gcc,我的问题分为三个部分 问题1 考虑下面的代码, #include <iostream> using namespace std; int main( int argc, char *argv[]) { const int v = 50; int i = 0X7FFFFFFF; cout<<(i + v)<<endl; if ( i + v < i ) { cout<<"Numbe

我的问题分为三个部分

问题1
考虑下面的代码,

#include <iostream>
using namespace std;

int main( int argc, char *argv[])
{

    const int v = 50;
    int i = 0X7FFFFFFF;

    cout<<(i + v)<<endl;

    if ( i + v < i )
    {
        cout<<"Number is negative"<<endl;
    }
    else
    {
        cout<<"Number is positive"<<endl;
    }

    return 0;
}
#包括
使用名称空间std;
int main(int argc,char*argv[])
{
常数v=50;
int i=0X7FFFFFFF;

cout这是一个已知的问题,我不认为这是编译器中的错误。当我使用gcc4.5和
-Wall-O2
编译时,它会发出警告

警告:假定(X+c) 尽管你的代码确实溢出了

您可以传递
-fno strict overflow
标志来关闭特定的优化。

行的作用是:

cout<<(i + v)<<endl;

coutQ1:也许,在64位实现中,这个数字确实是正数?谁知道呢?在调试代码之前,我刚刚打印了f(“%d”,I+v)

问题2:括号只是告诉编译器如何解析表达式。这通常是以树的形式完成的,因此优化器根本看不到任何括号。并且可以自由转换表达式


Q3:这就是为什么,作为C/C++程序员,你不能编写假定底层硬件的特定属性的代码,例如,int是32位的二进制补码形式。

< p>你的代码产生未定义的行为。C和C++语言没有“溢出机制”。对于有符号整数算术。您的计算溢出有符号整数-行为立即未定义。考虑到它形成“编译器中是否存在错误”的位置与尝试分析
i=i+++++++i
示例没有什么不同


GCC编译器基于C/C++语言规范的这一部分进行了优化,称为“严格溢出语义”这是基于这样一个事实,即在C++中加上正负号的正值总是产生较大的值或导致未定义的行为。这就意味着编译器完全可以假定总和总是较大。GCC中也存在优化。它们都导致了更多“黑客”的抱怨GCC用户社区的一部分人,他们中的许多人甚至不怀疑他们在C/C++程序中所依赖的技巧只是非法的黑客行为。

好吧,这差不多是六年前的事了,问题已经得到了回答。但我觉得有些地方还没有得到我满意的解决,所以我添加了一些评论,希望能给大家一些帮助这篇讨论的未来读者的利益(比如我自己,当我搜索到它时)

  • 使用gcc 4.1.2指定的OP没有任何特殊标志。我假设没有
    -O
    标志相当于
    -O0
    。没有要求优化,为什么gcc会以报告的方式优化代码?在我看来,这确实像一个编译器错误。我还假设这在以后的版本中已经修复(例如,一个答案提到gcc 4.5和
    -fno strict overflow
    优化标志)。当前的gcc手册页说明
    -fstrict overflow
    包含在
    -O2
    或更高版本中

  • 在当前版本的gcc中,有一个选项
    -fwrapv
    ,允许您使用导致OP出现问题的代码。当然,前提是您确保知道整数类型的位大小。从gcc手册页:

  • -fstrict溢出 ..... 另请参见-fwrapv选项。使用-fwrapv表示整数有符号溢出 是完全定义的:它用-fwrapv包装…某些类型的溢出是 允许。例如,如果编译器在执行算术时出现溢出 在常量上,溢出值仍然可以与-fwrapv一起使用,但不能与-fwrapv一起使用。
    +1.要强调的是,GCC的行为是一致的,因为标准规定此类操作中的溢出会导致未定义的行为。“假设有符号溢出”-事实上。在什么标准中规定,(0x7fffffff+50)必须溢出?标准不要求0x7FFFFFFFFF+50溢出。但是,它允许它溢出,如果它允许,那么标准允许未定义的行为。-fno严格溢出在所有gcc版本中都不存在。无论如何,这是我得到的最佳答案和选项。此外,我认为我对INT_MAX+x为负的假设是有效的(我不依赖于机器实现)。请让我知道你的想法。@ KUMAR M KIRAN:你的假设是无效的。C++标准称有符号整数溢出是未定义的行为(5/5,3.91/4,在这里声明无符号类型环绕。在java中,你说的是真的,C++中没有。发生在64位实现上。您有64位整数的系统示例吗?我认为这是非常罕见的。不,这不好,因为这是未定义的行为。您假设整数是2的补码中的32位量,并且MAX_int+一定是负数,这完全是错误的。也许您可以告诉我们您实际尝试的是什么为此,我们可以为您找到一种符合标准的方法。这是一个很好的例子,说明了为什么您不能说,“我知道我的处理器在签名溢出上进行了封装,因此它是安全的”。20年前,人们坚持认为0x7fff是最大正值……这真的没有什么新鲜事了。:)@Ingo:是的,但这不是问题所在:所讨论的实现确实有一个32位的
    int
    ,并且int_MAX确实等于0x7fffff。问题是程序溢出了一个有符号整数类型,行为未定义。在这种情况下,由于优化,行为是它得到的比较“错误”。大多数(全部?)64位机器仍然将“int”定义为32位。因此,如果递增值0x7fffffff,则值为0x7fffffff的int仍然会溢出。 -fstrict-overflow ..... See also the -fwrapv option. Using -fwrapv means that integer signed overflow is fully defined: it wraps. ... With -fwrapv certain types of overflow are permitted. For example, if the compiler gets an overflow when doing arithmetic on constants, the overflowed value can still be used with -fwrapv, but not otherwise.