为什么这个被零除错误只出现在优化代码中?

时间:2010-10-04 01:49:11

标签: c++ optimization compiler-construction divide-by-zero

我刚刚发现了一个奇怪的错误,只有在启用优化(g++ -O2)时才会发生。当Arithmetic exception设置为零(来自命令行参数)时,以下代码中为interval

for(int i = 0; i < n; ++i) {
  if((i + 1) % interval == 0) { // exception here
    DoSomething();
  }
}

很明显,模零操作会抛出除零异常,但为什么只有在编译优化时编译代码才会出现这种情况?

4 个答案:

答案 0 :(得分:13)

除以零始终是未定义的行为。使用不同的优化设置获得不同结果的事实仍然符合未定义行为的定义。

答案 1 :(得分:1)

持续折叠。

您将interval声明为全局const int,并且编译器接受了您的信息。

答案 2 :(得分:0)

您没有告诉我们'interval'的设置位置。优化器可能正在执行将“interval”设置为0的操作。将代码更改为

for(int i = 0; i < n; ++i) {
  if (0==interval) { break; }
  if((i + 1) % interval == 0) { // exception here
    DoSomething();
  }
}

看看你是否仍然得到错误。或者更好的是,向我们展示“间隔”的价值所在。

答案 3 :(得分:0)

您能举例说明问题吗?如果优化更改结果,则需要反汇编代码并比较差异。你的目标平台是什么? x86,arm,ppc?操作系统?等?

#include 
const int interval=BOB;
int main ( void )
{
    int i,n;
    n=10;
    for(i = 0; i < n; ++i)
    {
        if((i + 1) % interval == 0)
        { // exception here
            printf("%d\n",i);
        }
    }
    return(0);
}
gcc interval.c -DBOB=0 -O2 -o interval
interval.c: In function ‘main’:
interval.c:15: warning: division by zero

编译器发现了......

编辑:

如果您尝试从命令行参数分配它,您应该得到编译器错误,因此无法执行任何操作。

#include <stdio.h>
const int interval;
int main ( int argc, char *argv[] )
{
    int i,n;
    if(argc<2) return(1);
    interval=atoi(argv[1]);

    n=10;
    for(i = 0; i < n; ++i)
    {
        if((i + 1) % interval == 0)
        { // exception here
            printf("%d\n",i);
        }
    }
    return(0);
}
gcc -o interval interval.c
interval.c: In function ‘main’:
interval.c:7: error: assignment of read-only variable ‘interval’

请提供一个完整的例子。

很有可能使用const并使编译器工作意味着变量从错误的地址被拉出并获得在那里发生的任何事情,这可能是也可能不是零,这取决于该地址是什么以及所有其他地址你的代码更改优化设置会移动该地址的位置或指向的位置或执行期间更改的内容,直到更改结果为止。

编辑:

#include <stdio.h>
int main ( int argc, char *argv[] )
{
const int interval;
    int i,n;
    if(argc<2) return(1);
    interval=atoi(argv[1]);

    n=10;
    for(i = 0; i < n; ++i)
    {
        if((i + 1) % interval == 0)
        { // exception here
            printf("%d\n",i);
        }
    }
    return(0);
}

gcc -c interval.c 
interval.c: In function ‘main’:
interval.c:7: error: assignment of read-only variable ‘interval’

编译器仍然知道它是一个只读变量,使用地址指向非const变量,它不会改变其只读状态,只是摆脱了编译器错误,但仍然失败了跑。正如设计的那样,例如.text被放置在只读存储器(rom / flash)中,那么无论你玩多少寻址和指针游戏,你都无法改变它的运行时间,直到你删除const并使它成为一个读/写变量。这样的指针操作无论如何都是一个主要的罪,因为它可以并且最终会在你优化的时候失败(如果你使用一个非常好的编译器而不一定是gcc,虽然它也在gcc上也失败了)(99.999999999999%的时候它运气是否有效,但在失败时可以解释并指向软件设计而不是编译器或语言。除非const是这个问题的根本原因,否则只需删除const并给我们一个演示问题的完整示例。在一个下午或一天内,这可能会被关闭。

编辑2:

unsigned int fun  ( unsigned int a )
{
    const unsigned int b = 7;
    *(unsigned int *)&b = 5;
    return(a+b);
}

通过优化编译上面的内容,你得到:

    .global fun
fun:
    add r0, r0, #7
    bx  lr

如预期的那样,const使b只读。没有const:

unsigned int fun  ( unsigned int a )
{
    unsigned int b = 7;
    *(unsigned int *)&b = 5;
    return(a+b);
}
    .global fun
fun:
    add r0, r0, #5
    bx  lr

我对此感到惊讶,但从未如此表明const是如何工作的。