Does integer overflow cause undefined behavior because of memory corruption?

I recently read that signed integer overflow in C and C++ causes undefined behavior:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.

I am currently trying to understand the reason of the undefined behavior here. I thought undefined behavior occurs here because the integer starts manipulating the memory around itself when it gets too big to fit the underlying type.

So I decided to write a little test program in Visual Studio 2015 to test that theory with the following code:

#include <stdio.h>
#include <limits.h>

struct TestStruct
{
    char pad1[50];
    int testVal;
    char pad2[50];
};

int main()
{
    TestStruct test;
    memset(&test, 0, sizeof(test));

    for (test.testVal = 0; ; test.testVal++)
    {
        if (test.testVal == INT_MAX)
            printf("Overflowingrn");
    }

    return 0;
}

I used a structure here to prevent any protective matters of Visual Studio in debugging mode like the temporary padding of stack variables and so on. The endless loop should cause several overflows of test.testVal , and it does indeed, though without any consequences other than the overflow itself.

I took a look at the memory dump while running the overflow tests with the following result ( test.testVal had a memory address of 0x001CFAFC ):

0x001CFAE5  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x001CFAFC  94 53 ca d8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

溢出的整数与内存转储

As you see, the memory around the int that is continuously overflowing remained "undamaged". I tested this several times with similar output. Never was any memory around the overflowing int damaged.

What happens here? Why is there no damage done to the memory around the variable test.testVal ? How can this cause undefined behavior?

I am trying to understand my mistake and why there is no memory corruption done during an integer overflow.


You misunderstand the reason for undefined behavior. The reason is not memory corruption around the integer - it will always occupy the same size which integers occupy - but the underlying arithmetics.

Since signed integers are not required to be encoded in 2's complement, there can not be specific guidance on what is going to happen when they overflow. Different encoding or CPU behavior can cause different outcomes of overflow, including, for example, program kills due to traps.

And as with all undefined behavior, even if your hardware uses 2's complement for its arithmetic and has defined rules for overflow, compilers are not bound by them. For example, for a long time GCC optimized away any checks which would only come true in a 2's-complement environment. For instance, if (x > x + 1) f() is going to be removed from optimized code, as signed overflow is undefined behavior, meaning it never happens (from compiler's view, programs never contain code producing undefined behavior), meaning x can never be greater than x + 1 .


The authors of the Standard left integer overflow undefined because some hardware platforms might trap in ways whose consequences could be unpredictable (possibly including random code execution and consequent memory corruption). Although two's-complement hardware with predictable silent-wraparound overflow handling was pretty much established as a standard by the time the C89 Standard was published (of the many reprogrammable-microcomputer architectures I've examined, zero use anything else) the authors of the Standard didn't want to prevent anyone from producing C implementations on older machines.

On implementations which implemented commonplace two's-complement silent-wraparound semantics, code like

int test(int x)
{
  int temp = (x==INT_MAX);
  if (x+1 <= 23) temp+=2;
  return temp;
}

would, 100% reliably, return 3 when passed a value of INT_MAX, since adding 1 to INT_MAX would yield INT_MIN, which is of course less than 23.

In the 1990s, compilers used the fact that integer overflow was undefined behavior, rather than being defined as two's-complement wrapping, to enable various optimizations which meant that the exact results of computations that overflowed would not be predictable, but aspects of behavior that didn't depend upon the exact results would stay on the rails. A 1990s compiler given the above code might likely treat it as though adding 1 to INT_MAX yielded a value numerically one larger than INT_MAX, thus causing the function to return 1 rather than 3, or it might behave like the older compilers, yielding 3. Note that in the above code, such treatment could save an instruction on many platforms, since (x+1 <= 23) would be equivalent to (x <= 22). A compiler may not be consistent in its choice of 1 or 3, but the generated code would not do anything other than yield one of those values.

Since then, however, it has become more fashionable for compilers to use the Standard's failure to impose any requirements on program behavior in case of integer overflow (a failure motivated by the existence of hardware where the consequences might be genuinely unpredictable) to justify having compilers launch code completely off the rails in case of overflow. A modern compiler could notice that the program will invoke Undefined Behavior if x==INT_MAX, and thus conclude that the function will never be passed that value. If the function is never passed that value, the comparison with INT_MAX can be omitted. If the above function were called from another translation unit with x==INT_MAX, it might thus return 0 or 2; if called from within the same translation unit, the effect might be even more bizarre since a compiler would extend its inferences about x back to the caller.

With regard to whether overflow would cause memory corruption, on some old hardware it might have. On older compilers running on modern hardware, it won't. On hyper-modern compilers, overflow negates the fabric of time and causality, so all bets are off. The overflow in the evaluation of x+1 could effectively corrupt the value of x that had been seen by the earlier comparison against INT_MAX, making it behave as though the value of x in memory had been corrupted. Further, such compiler behavior will often remove conditional logic that would have prevented other kinds of memory corruption, thus allowing arbitrary memory corruption to occur.


Undefined behaviour is undefined. It may crash your program. It may do nothing at all. It may do exactly what you expected. It may summon nasal demons. It may delete all your files. The compiler is free to emit whatever code it pleases (or none at all) when it encounters undefined behaviour.

Any instance of undefined behaviour causes the entire program to be undefined - not just the operation that is undefined, so the compiler may do whatever it wants to any part of your program. Including time travel: Undefined behavior can result in time travel (among other things, but time travel is the funkiest).

There are many answers and blog posts about undefined behaviour, but the following are my favorites. I suggest reading them if you want to learn more about the topic.

  • A Guide to Undefined Behavior in C and C++, Part 1
  • What Every C Programmer Should Know About Undefined Behavior #1/3
  • 链接地址: http://www.djcxy.com/p/79926.html

    上一篇: nostdlib在SDL中导致分段错误

    下一篇: 整数溢出是否会因为内存损坏而导致未定义的行为?