简体   繁体   English

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

[英]Why does this divide-by-zero error only occur in optimized code?

I just found a bug that, strangely, occurred only when optimization was turned on ( g++ -O2 ). 我刚刚发现了一个奇怪的错误,只有在打开优化时才会出现这个错误( g++ -O2 )。 It was an Arithmetic exception in the following code, when interval was set to zero (from a command line argument): interval设置为零(从命令行参数)时,以下代码中是Arithmetic exception

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

It's obvious that modulo zero operation threw a division-by-zero exception, but why did this only occur when the code was compiled with optimization turned on? 很明显,模零操作会抛出一个被零除的异常,但为什么只有在代码编译时才会启用优化?

Divide by zero is always undefined behavior. 除以零始终是未定义的行为。 The fact that you get different results with different optimization settings still fits within the definition of undefined behavior. 使用不同的优化设置获得不同结果的事实仍然适合未定义行为的定义。

Constant folding. 不断折叠。

You declared interval as a global const int and compiler took you at your word. 你声明interval是一个全局的const int,编译器会把你的话告诉你。

You don't show us where 'interval' gets set. 您没有告诉我们'interval'的设置位置。 The optimizer may be doing something that sets 'interval' to 0. Change your code to 优化器可能正在执行将“interval”设置为0的操作。将代码更改为

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

And see if you still get the error. 并看看你是否仍然得到错误。 Or better yet, show us where 'interval' is getting its value. 或者更好的是,告诉我们'间隔'在哪里获得它的价值。

Can you provide an example that demonstrates the problem? 你能提供一个证明这个问题的例子吗? If optimization changes the results then you need to disassemble the code and compare the difference. 如果优化更改结果,则需要反汇编代码并比较差异。 What is your target platform? 你的目标平台是什么? x86, arm, ppc? x86,arm,ppc? operating system? 操作系统? etc? 等等?

#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

Compiler figured it out... 编制者想通了......

EDIT: 编辑:

If you try to assign it from a command line argument you should get a compiler error as a result there isnt anything to execute. 如果您尝试从命令行参数分配它,您应该得到编译器错误,因此无法执行任何操作。

#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’

Please provide a complete example. 请提供一个完整的例子。

It is quite possible that using const and getting the compiler to work means the variable is being pulled from the wrong address and getting whatever happens to be there which may or may not be zero depending on what that address is and all the rest of your code. 很有可能使用const并使编译器工作意味着变量从错误的地址被拉出并获得在那里发生的任何事情,这可能是也可能不是零,这取决于该地址是什么以及代码的所有其余部分。 changing the optimization settings moves where that address is or what it points to or what it is changed to during execution up to that point changing the results. 更改优化设置会移动该地址的位置或指向的位置或执行期间更改的内容,直到更改结果为止。

EDIT: 编辑:

#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’

The compiler still knows that it is a read-only variable, using the address to point a non-const variable at it does not change its read-only state, just gets rid of the compiler error and still fails in the long run. 编译器仍然知道它是一个只读变量,使用地址指向非const变量,它不会改变其只读状态,只是摆脱了编译器错误,但从长远来看仍然失败。 As designed if for example .text is placed in read-only memory (rom/flash) then no matter how many addressing and pointer games you play you wont be able to change it run time, until you remove const and make it a read/write variable. 正如设计的那样,例如.text被放置在只读存储器(rom / flash)中,那么无论你玩多少寻址和指针游戏,你都无法改变它的运行时间,直到你删除const并使它成为一个读/写变量。 Pointer manipulation like that is a cardinal sin anyway because it can and will eventually fail if/when you optimize (if when you use a really good compiler and not necessarily gcc, although it fails on gcc as well)(99.999999999999% of the time it is luck that it works, but very explainable when it fails and points to the software design not the compiler or language). 这样的指针操作无论如何都是一个主要的罪,因为它可以并且最终会在你优化的时候失败(如果你使用一个非常好的编译器而不一定是gcc,虽然它也在gcc上也失败了)(99.999999999999%的时候它运气是否有效,但在失败时可以解释并指向软件设计而不是编译器或语言。 Unless the const is the root cause of this question, just remove the const and give us a complete example that demonstrates the problem. 除非const是这个问题的根本原因,否则只需删除const并给我们一个演示问题的完整示例。 Within an afternoon or day this could be closed. 在一个下午或一天内,这可能会被关闭。

EDIT 2: 编辑2:

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

compile the above with optimization and you get: 通过优化编译以上内容,您将得到:

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

as expected, the const makes b read only. 正如所料,const使b只读。 without the const: 没有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

Which I am surprised by but never-the-less demonstrates how const works. 我对此感到惊讶,但从未如此表明const是如何工作的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM