为什么这个被零除的错误只发生在优化的代码中?

问题描述 投票:0回答:4

我刚刚发现了一个错误,奇怪的是,该错误仅在打开优化时才会出现(

g++ -O2
)。当
Arithmetic exception
设置为零(来自命令行参数)时,它是以下代码中的
interval

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

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

c++ optimization divide-by-zero
4个回答
13
投票

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


1
投票

不断折叠。

您将interval声明为全局const int,编译器相信您的话。


0
投票

您没有向我们展示“间隔”的设置位置。优化器可能会执行将“间隔”设置为 0 的操作。将代码更改为

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

看看是否仍然出现错误。或者更好的是,向我们展示“间隔”在哪里获得其价值。


0
投票

你能提供一个例子来说明这个问题吗? 如果优化改变了结果,那么您需要反汇编代码并比较差异。 您的目标平台是什么? x86、arm、ppc? 操作系统?等等?

#包括 
常量 int 间隔=BOB;
int main ( 无效 )
{
    整数 i,n;
    n=10;
    对于(i = 0; i < n; ++i)
    {
        if((i + 1) % interval == 0)
        { // exception here
            printf("%d\n",i);
        }
    }
    return(0);
}
gcc 间隔.c -DBOB=0 -O2 -o 间隔
Interval.c:在函数“main”中:
Interval.c:15:警告:除以零

编译器弄清楚了...

编辑:

如果您尝试从命令行参数分配它,您应该会收到编译器错误,因为没有任何东西可以执行。

#包括
常量 int 间隔;
int main ( int argc, char *argv[] )
{
    整数 i,n;
    如果(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.c
Interval.c:在函数“main”中:
Interval.c:7:错误:只读变量“interval”的赋值

请提供完整的示例。

使用 const 并让编译器工作很可能意味着该变量是从错误的地址中提取出来的,并获取那里发生的任何内容,该变量可能为零也可能不为零,具体取决于该地址是什么以及您的所有其余部分代码。 更改优化设置会移动该地址所在的位置或它指向的内容或在执行期间更改为的内容,直到更改结果。

编辑:

#包括
int main ( int argc, char *argv[] )
{
常量 int 间隔;
    整数 i,n;
    如果(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 间隔.c 
Interval.c:在函数“main”中:
Interval.c:7:错误:只读变量“interval”的赋值

编译器仍然知道它是一个只读变量,使用地址将非常量变量指向它并不会改变其只读状态,只是摆脱编译器错误,从长远来看仍然会失败。 按照设计,如果 .text 被放置在只读存储器(ROM/闪存)中,那么无论您玩多少次寻址和指针游戏,您都将无法更改它的运行时间,直到您删除 const 并将其设为读取/写入变量。 无论如何,像这样的指针操作是一个大罪,因为如果/当你优化时它可能并且最终会失败(如果当你使用一个非常好的编译器而不一定是 gcc 时,尽管它在 gcc 上也会失败)(99.999999999999%的时间它它能工作是幸运的,但当它失败并指向软件设计而不是编译器或语言时,这是可以解释的)。 除非 const 是这个问题的根本原因,否则只需删除 const 并给我们一个完整的示例来演示该问题。 一个下午或一天之内,这可能会被关闭。

编辑2:

无符号整型乐趣(无符号整型a)
{
    常量无符号整数 b = 7;
    *(无符号整数*)&b = 5;
    返回(a+b);
}

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

 .全球乐趣
乐趣:
    添加 r0, r0, #7
    bxlr

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

无符号整型乐趣(无符号整型a)
{
    无符号整型 b = 7;
    *(无符号整数*)&b = 5;
    返回(a+b);
}
 .全球乐趣
乐趣:
    添加 r0, r0, #5
    bxlr

我对此感到惊讶,但仍然展示了 const 的工作原理。

© www.soinside.com 2019 - 2024. All rights reserved.