Ancient Dragon:
You state that you can take address of a reference.
It is true that you can take the address of an expression formed of an identifier of a variable whose type is a reference type
In a sense, it is the address of the expression.
But can we say that it is taking the address of the reference.
If, by reference, we mean expression, that's right.
If, by reference, we mean the reference object, it is wrong.
Because, in fact, it yields the address of the object denoted by the reference.
Now, I think that your post #5 is very misleading.
You seem to say that foo1 and foo2, are, in a sense equivalent.
First, reference might be implemented in a very different way than the address of the object, though that's the implementation of a lot of compilers.
Now, deeming a well-defined x86-32 compiler (GCC 3.2.3), I can say that foo1 and foo2 are different beasts.
This compiler implements pointers as native (as in assembly) addresses of the object pointed-to.
And it implements references as the address of the object refered-to by the reference.
I compiled your code (with g++ -g) without optimizations:
Here is the disassembled code of foo1 (comments are mine)
0x4012d0 <_Z4foo1Ri>: push %ebp
0x4012d1 <_Z4foo1Ri+1>: mov %esp,%ebp
0x4012d3 <_Z4foo1Ri+3>: sub $0x4,%esp // allocates space for p
0x4012d6 <_Z4foo1Ri+6>: mov 0x8(%ebp),%eax // moves address of the refered-to object in %eax
0x4012d9 <_Z4foo1Ri+9>: mov %eax,0xfffffffc(%ebp) // and then, move it in the storage allocated for p, now p=&original_object
0x4012dc <_Z4foo1Ri+12>: mov 0xfffffffc(%ebp),%eax // fetch the pointer into %eax
0x4012df <_Z4foo1Ri+15>: movl $0x7b,(%eax) // moves 0x7b in the pointed-to object.
0x4012e5 <_Z4foo1Ri+21>: leave
0x4012e6 <_Z4foo1Ri+22>: ret
0x4012e8 <_Z4foo2Pi>: push %ebp
0x4012e9 <_Z4foo2Pi+1>: mov %esp,%ebp
0x4012eb <_Z4foo2Pi+3>: sub $0x4,%esp // allocates space for p (pointer to pointer to int)
0x4012ee <_Z4foo2Pi+6>: lea 0x8(%ebp),%eax // moves the address of the pointer (which is on the stack) x in %eax
0x4012f1 <_Z4foo2Pi+9>: mov %eax,0xfffffffc(%ebp) // then move this address in the storage allocated for p
0x4012f4 <_Z4foo2Pi+12>: mov 0xfffffffc(%ebp),%eax // fetch the value of p in %eax; now %eax contains a pointer to a pointer (which is the x argument) to int (which is abc)
0x4012f7 <_Z4foo2Pi+15>: mov (%eax),%eax // now %eax is a pointer to int (which points to abc)
0x4012f9 <_Z4foo2Pi+17>: movl $0x159,(%eax) // moves $0x159 in abc
0x4012ff <_Z4foo2Pi+23>: leave
0x401300 <_Z4foo2Pi+24>: ret
You see that these codes are different.
In fact, the foo1 is much more equivalent to the following foo3:
void foo3(int* x) {
int* p=x;
*p=123;
}
int main() {
int i=486;
foo3(&i);
return 0;
}
Now, I compiled foo3 (g++ -g)
The code of foo3 is:
Dump of assembler code for function _Z4foo3Pi:
0x4012d0 <_Z4foo3Pi>: push %ebp
0x4012d1 <_Z4foo3Pi+1>: mov %esp,%ebp
0x4012d3 <_Z4foo3Pi+3>: sub $0x4,%esp
0x4012d6 <_Z4foo3Pi+6>: mov 0x8(%ebp),%eax
0x4012d9 <_Z4foo3Pi+9>: mov %eax,0xfffffffc(%ebp)
0x4012dc <_Z4foo3Pi+12>: mov 0xfffffffc(%ebp),%eax
0x4012df <_Z4foo3Pi+15>: movl $0x7b,(%eax)
0x4012e5 <_Z4foo3Pi+21>: leave
0x4012e6 <_Z4foo3Pi+22>: ret
foo3 has the exact same code than foo1.
It doesn't mean that foo1 and foo3 are equivalent on all platforms (I know that references & pointers are different things, even if they are often implemented in a similar way).
But I think it is clear that foo1 and foo2 are different things.
Now, with optimizations... Since this code is dumb, the compiler can generate the same code:
With "g++ -g -O"
foo1 code is
0x4012d0 <_Z4foo1Ri>: push %ebp
0x4012d1 <_Z4foo1Ri+1>: mov %esp,%ebp
0x4012d3 <_Z4foo1Ri+3>: mov 0x8(%ebp),%eax
0x4012d6 <_Z4foo1Ri+6>: movl $0x7b,(%eax)
0x4012dc <_Z4foo1Ri+12>: pop %ebp
0x4012dd <_Z4foo1Ri+13>: ret
foo2 code is:
0x4012de <_Z4foo2Pi>: push %ebp
0x4012df <_Z4foo2Pi+1>: mov %esp,%ebp
0x4012e1 <_Z4foo2Pi+3>: mov 0x8(%ebp),%eax
0x4012e4 <_Z4foo2Pi+6>: movl $0x159,(%eax)
0x4012ea <_Z4foo2Pi+12>: pop %ebp
0x4012eb <_Z4foo2Pi+13>: ret
That is the same code (except that the constant $0x7b or $0x159 is not the same).
Because the compiler has been able to optimize out the intermediate p pointer-to-pointer variable in foo2.
With higher level of optimizations, inlining appears, and a lot of operations can be optimized out.