-1

Could someone explain why the following code behaves differently if the 3 first printf calls are there or not ?

#include <stdlib.h>
#include <stdio.h>

int main(){
    int a = 1;
    int b = 2;
    int c = 3;
    
    int* a_ptr = &a;
    
    // comment out the following 3 lines
    printf(" &a : %p\n", &a);
    printf(" &b : %p\n", &b);
    printf(" &c : %p\n", &c);


    printf("*(a_ptr)   : %d\n", *(a_ptr));
    printf("*(a_ptr-1) : %d\n", *(a_ptr-1));
    printf("*(a_ptr-2) : %d\n", *(a_ptr-2));

I was playing around to learn how variables are being stacked in memory depending on the order of declaration. By printing the addresses, I see they are 1 int size apart if they are declared after each other. After printing the addreses, I just subtract 1 and 2 from the address of a and dereference it. When I print it, it shows what i'd expect, the values of a, b and c. BUT, if i do not print the addresses before, I just get a=1, while b and c seem random to me. What is going on here ? Do you get the same behaviour on your machine ?

// without printing the addresses
*(a_ptr)   : 1
*(a_ptr-1) : 4200880
*(a_ptr-2) : 6422400
 
// with printing 
 &a : 0061FF18
 &b : 0061FF14
 &c : 0061FF10
*(a_ptr)   : 1
*(a_ptr-1) : 2
*(a_ptr-2) : 3
JustANoob
  • 580
  • 1
  • 4
  • 19
  • 2
    It is an Undefined Behaviuir. How variables are stored is implementation defined, they do not have to be stored at all which is the case here . They are just simple optimized out. – 0___________ Oct 25 '20 at 01:50
  • Actually, this is not an array, thus they might not be loaded consecutively and in order, it is completely the bound to the OS and theorytically, the addresses of each variable can be random. If you repeat with-printig example at different times, it could also print unrelated numbers. – Furkan Çetinkaya Oct 25 '20 at 01:51
  • @P__J__: “Implementation defined” in the C standard means the standard requires implementations to document how they behave. How objects are stored is not implementation defined. It is not specified by the C standard. – Eric Postpischil Oct 25 '20 at 01:53
  • What Platform is your test case above compiled on? You might try it out on other platforms if you have access to them. For example, using GCC on Linux x86_64, the addresses for a, b, c, climb, rather than descend. As covered in other comments, it is implementation defined, but you can prove that to yourself by trying it out. – NateB Oct 25 '20 at 01:54
  • @EricPostpischil where does standard mention stack and how and where automatic variables are stored? C standard even does not know existance of the stack – 0___________ Oct 25 '20 at 10:13

1 Answers1

3

The C standard does not define what happens in the code you show, notably because the address arithmetic a_ptr-1 and a_ptr-2 is not defined. For an array of n elements, pointer arithmetic is defined only for adjusting pointers to locations from that corresponding to index 0 to that corresponding to index n (which is one beyond the last element of the array). For a single object, pointer arithmetic is defined as if it were an array of one element, so only for adjusting a pointer to the locations corresponding to index 0 and index 1 (just beyond the object). a_ptr-1 and a_ptr-2 would point to elements at indices −1 and −2, and the C standard does not define the behavior for these.

However, what is happening in the experiments you tried is:

  • When the addresses of a, b, and c are printed, the compiler has to ensure there are addresses for it to print. So it assigns memory locations to a, b, and c. It happens that these are consecutive in memory and in reverse order, and therefore a_ptr-1 happened to point to b and a_ptr-2 happened to point to c. (However, compilers may assign locations in memory based on alphabetical order of the names rather than declaration order, based on alignment and size and other properties, based on some arbitrary hash it uses to organize the names in a data structure, and/or other factors. In particular, if you compiled without optimization, and you request high optimization instead, the order may change.)
  • When the addresses are not printed, the compiler has no need to assign memory locations for b and c because they are not used in the code. In this case, a_ptr-1 and a_ptr-2 do not point to b and c but point to locations in memory that have been used for other purposes.
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • This all answer can be reduced to: standard does not specify how variables are stored, or if they have to be stored at all. The answer of this question was in the closing duplicate about optimized out variables – 0___________ Oct 25 '20 at 10:18