-1

Assume I have the following C code

#include <stdio.h>

int main(){ 
 int a = 5;
 int b = 6;
 int c = 7;
 void *d = &a;
 long *ld = (long *)d;
 printf("%ld\n", *ld);
 return 0;
}

Doing a sizeof using gdb, I can see that an int is 4 bytes and a long is 8 bytes on my little endian, x64 machine.

The output of the above printf is 5.

My question is, how is the value 5 despite the fact that my ld pointer is now 64 bits and pointing to the first byte of a (a 32 bit varable). How is it not overflowing when i'm dereferencing it? How is it not including the bytes in b and c? How does it know to stop counting after 32 bits although it's a 64 bit pointer?

Joe
  • 1
  • 1
  • 3
    It's undefined behavior. *Anything* can happen, even producing *seemingly* "correct" results. Also see https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior – jamesdlin Feb 22 '19 at 04:18
  • What makes you think the bytes of `b` are on the side of `a` that gets picked up with the `long`? What makes you think the bytes of `b` are anywhere near `a` at all? What makes you think `b` is given any bytes at all since it is not used and could have been removed by optimization? – Eric Postpischil Feb 22 '19 at 04:23
  • @EricPostpischil they are all on the stack right after another, I confirmed it wit gdb – Joe Feb 22 '19 at 04:25
  • This is 2 different forms of undefined behavior and reasoning about the results is completely uninteresting, as there are no guarantees of any output. – Lundin Feb 22 '19 at 07:33
  • 3
    [What is the strict aliasing rule?](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) – Lundin Feb 22 '19 at 07:37
  • @Joe: Second, what makes you think the bytes of `b` are on the side of `a` that gets picked up with the `long`? First, you should be aware that C is not a language for specifying specific machine instructions. The C standard defines C in terms of an abstract machine. In that machine, `a`, `b`, and `c` are assigned arbitrary locations, not pushed to a stack in order. When the compiler compiles and optimizes, it may implement that machine in any way. In a simple compilation with no optimization, it may happen to push them to a stack in order. In general, you **cannot** rely on that happening. – Eric Postpischil Feb 22 '19 at 12:52

2 Answers2

1

It is overflowing, and, as noted in first comment, it is undefined behavior.

There may be different reasons not to reach b or c. First, as they are not used, they may be optimized away. If they were used, then, as long as you don't get address of them, they may reside in register. If they are on stack, order of variables on stack could be different, and there is also padding added possible.

Joining these to a single structure may make optimization and re-ordering to be obstructed. Making the variable of this structure type global is even more likely to work. But anyway nothing is guaranteed, since it is still undefined behavior.

Alex Guteniev
  • 12,039
  • 2
  • 34
  • 79
1

The behave should be compiler and flag dependent. For example your exact code in my test:

% gcc x.c -W -Wall -O2
% ./a.out 
5
% gcc x.c -W -Wall -O0
% ./a.out 
-90829968375803

The memory that stores a, b, c, may be optimized out or rearranged, realigned on the stack depends on compiler and flags.

KL-Yang
  • 381
  • 4
  • 8