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

发布于 2024-09-26 14:06:01 字数 317 浏览 4 评论 0原文

我刚刚发现了一个错误,奇怪的是,该错误仅在打开优化时才会出现(g++ -O2)。当 interval 设置为零(来自命令行参数)时,以下代码中出现了一个算术异常

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

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

I just found a bug that, strangely, occurred only when optimization was turned on (g++ -O2). It was an Arithmetic exception in the following code, when interval was set to zero (from a command line argument):

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

吾性傲以野 2024-10-03 14:06:01

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

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.

送你一个梦 2024-10-03 14:06:01

不断折叠。

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

Constant folding.

You declared interval as a global const int and compiler took you at your word.

葬花如无物 2024-10-03 14:06:01

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

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

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

You don't show us where 'interval' gets set. The optimizer may be doing something that sets 'interval' to 0. Change your code to

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.

两仪 2024-10-03 14:06:01

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

编译器弄清楚了...

编辑:

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

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

编译器仍然知道它是一个只读变量,使用地址指向一个非常量变量不会改变它的只读状态,只是摆脱编译器错误,从长远来看仍然会失败。按照设计,如果 .text 被放置在只读存储器(ROM/闪存)中,那么无论您玩多少次寻址和指针游戏,您都将无法更改它的运行时间,直到您删除 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 的工作原理。

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? 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. 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. 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. 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). Unless the const is the root cause of this question, just remove the const and give us a complete example that demonstrates the problem. Within an afternoon or day this could be closed.

EDIT 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. without the 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文