简体   繁体   中英

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 ). 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?

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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