In your second code, the compiler is trying to access:
va.ptr[0]
The compiler can infer that va.ptr matches &a[0] , and since a is a nonvolatile local variable main , it also knows that you are not changing a[0] ( test does not have βaccessβ to a ), so it can shorten your code to just calling test with a constant value.
In your first code, however, the compiler knows that it is trying to access:
*(((double**)&a)[index])
Although ((double**)&a)[index] can be output by the compiler (this value is dependent on the compiler), you will get a pointer to an address like 0x3ff0000000000000 (on my computer). What the above expression is trying to do is access the value stored at this address, but that value can be changed to test or even something else. There is no reason the compiler could have suggested that the value at this address does not change between the first access and the second.
Please note: if instead of double** you used double (*)[2] , you would get the same result as the second code, and your code would be correctly generated.
Your first code is basically equivalent:
extern "C" int test(const double a); int main() { double a[2] = { 1.0, 2.0 }; double **pp = (double**)&a; double *p = pp[0]; double a1 = *p; test(a1); double a2 = *p; test(a2); }
You will get the same disassembly using the command line.
Assuming an architecture with 4 double bytes and pointers, you get something like this when executed:
0x7fff4f40 0x3f800000 # 1.0 0x7fff4f44 0x40000000 # 2.0
Since a is an array of double , &a can decay into a double (*)[2] "with the value" 0x7fff4f40 .
Now you convert &a to double** , so you will have double **pp with a value of 0x7fff4f40 . From here, you extract double *p with pp[0] , since the pointer also has 4 bytes on my hypothetical architectures, you get 0x3f800000 .
Great, so the compiler can optimize before that, basically it can create something like this:
double *p = (double*) 0x3f800000; double a1 = *p; test(a1); double a2 = *p; test(a2);
Know the one million dollar question: what is at 0x3f80000 ? Well, no one knows, not even a compiler. The value at this address can be changed at any time by calling test() or even an external source.
I do not understand the size restrictions on double and pointer types, but suppose a hypothetical architecture where sizeof(double*) > 2 * sizeof(double) , the compiler will not even be able to output p , because you are trying to access values ββoutside a .