8

Without -O2 this code prints 84 84, with O2 flag the output is 84 42. The code was compiled using gcc 4.4.3. on 64-bit Linux platform. Why the output for the following code is different?

Note that when compiled with -Os the output is 0 42

#include <iostream>
using namespace std;

int main() {
    long long n = 42;
    int *p = (int *)&n;
    *p <<= 1;
    cout << *p << " " << n << endl;
    return 0;
}
Leonid
  • 22,360
  • 25
  • 67
  • 91
  • g++ 4.5.2 on a x86_64 linux prints `84 84` with -O0, -O1, -O2, -O3, and -Os – Cubbi Mar 04 '11 at 15:32
  • g++ 4.5.0 on 64-bit Windows prints `84 84` with any optimization level. – Leonid Mar 04 '11 at 15:34
  • I believe the result of this line `int *p = (int *)&n;` depends on endian-ness of the machine! – Nawaz Mar 04 '11 at 15:34
  • Why are you using an `int*` to a `long long`? – Justin Mar 04 '11 at 15:35
  • I'm able to reproduce the problem with g++ 4.4.4. Looks like the 4.4 branch has a bug/feature that was fixed in 4.5. – casablanca Mar 04 '11 at 15:36
  • @Nawaz, still, why would optimization mode influence the result? – Leonid Mar 04 '11 at 15:36
  • @Leonid try compiling with `-fno-strict-aliasing` – Cubbi Mar 04 '11 at 15:38
  • @Cubbi: that's circumventing a language rule... I'd rather avoid that. – Matthieu M. Mar 04 '11 at 15:41
  • @Leonid: because optimization are taking advantage of the aliasing rule to optimize... – Matthieu M. Mar 04 '11 at 15:41
  • @Matthieu M. I was going to say that too, but then got distracted reading other SO aliasing threads: http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule and http://stackoverflow.com/questions/262379/when-is-char-safe-for-strict-pointer-aliasing – Cubbi Mar 04 '11 at 15:51
  • @Cubbi: yes, it comes up now and then on SO, whether because got bit by it or because they suddenly experience this weird warning message :p – Matthieu M. Mar 04 '11 at 16:06

3 Answers3

19

When you use optimization with gcc, it can use certain assumptions based on the type of expressions to avoid repeating unnecessary reads and to allow retaining variables in memory.

Your code has undefined behaviour because you cast a pointer to a long long (which gcc allows as an extenstion) to a pointer to an int and then manipulate the pointed-to-object as if it were an int. A pointer-to-int cannot normally point to an object of type long long so gcc is allowed to assume that an operation that writes to an int (via a pointer) won't affect an object that has type long long.

It is therefore legitimate of it to cache the value of n between the time it was originally assigned and the time at which it is subsequently printed. No valid write operation could have changed its value.

The particular switch and documentation to read is -fstrict-aliasing.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 1
    Beat me to it :) Wikipedia gives some explanation: http://en.wikipedia.org/wiki/Pointer_aliasing where one can read *In C++, pointer arguments are assumed not to alias if they point to fundamentally different types ("strict aliasing" rules). This allows more optimizations to be done than in C.* Note that there is a special rule for `char*` which is allowed to alias any pointer. – Matthieu M. Mar 04 '11 at 15:39
  • 1
    @Matthieu: what's Wikipedia on about, "more optimization than in C"? C has strict aliasing rules too (since C99), although to be honest I've never actually compared with C++ side-by-side to check they're the same. If I'm doing something borderline I either (a) don't, or else (b) become a temporary expert on the rules for the language I'm doing it in, and then forget the exact details again shortly afterwards. – Steve Jessop Mar 04 '11 at 16:45
  • @Steve: actually, if you get to the article, they have distinguished C and C99 (`restrict`). As I am no C expert (C++ is already difficult enough for my lil' brain), I'd be hardpressed to answer your question though! – Matthieu M. Mar 04 '11 at 17:48
  • @Matthieu: ah, right, so the Wikipedia article is merely more out of date than the age of Wikipedia. Happens ;-) – Steve Jessop Mar 04 '11 at 18:33
6

You're breaking strict aliasing. Compiling with -Wall should give you a dereferencing type-punned pointer warning. See e.g. http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

Erik
  • 88,732
  • 13
  • 198
  • 189
1

I get the same results with GCC 4.4.4 on Linux/i386.

The program's behavior is undefined, since it violates the strict aliasing rule.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836