Is this :
void foo(int& x){
....
}
....
foo(arg);
the same as this :
void foo(int * x){
....
}
....
foo(&arg);
Do they lead to the same result ? Is the argument in the first one also passed by reference ?
Is this :
void foo(int& x){
....
}
....
foo(arg);
the same as this :
void foo(int * x){
....
}
....
foo(&arg);
Do they lead to the same result ? Is the argument in the first one also passed by reference ?
The &
sign can be used either with a type in a declaration, e.g. like this:
int& x; // #1
or before an identifier, e.g. like this:
&x // #2
The first variant means that you declare a variable as a reference (in this case a reference to int
).
The second variant means that you call the address-of operator which returns the memory address of the data.
The two uses of &
is entirely unrelated and has nothing to do with each other. They just use the same sign.
In your first example you declare a function taking a parameter by reference to int
(&
used when declaring type):
void foo(int& x) {
/* ... */
}
/* ... */
foo(arg); // Pass 'arg' to function.
In your second example you declare a function taking a parameter by pointer to int
, and later you use the address-of operator on arg
before passing it (effectively passing the address of arg
):
void foo(int* x) {
/* ... */
}
/* ... */
foo(&arg); // Pass address of 'arg' to function.
Related:
I do think this is a good question, and i disagree with the other people reactions. In my opinion, both functions have an integer parameter passed by reference (i.e., the adress of the variable is pushed on the called stack). The use of the ampersand is simply C++ syntactic sugar that is not available in the C standard.
To convince you that these two syntax have exactly the same behavior, you may compile and desassembly the following code:
void function_1(int &x){
x=3;
}
void function_2(int *x){
*x=3;
}
int main(void){
return 0;
}
After compilation and desassembly with
g++ -g -Wall -Werror ptr.c -o ptr
and
objdump -D ptr > res.txt
, you get the following assembly sections:
00000000004004b4 <_Z10function_1Ri>:
4004b4: 55 push %rbp
4004b5: 48 89 e5 mov %rsp,%rbp
4004b8: 48 89 7d f8 mov %rdi,-0x8(%rbp)
4004bc: 48 8b 45 f8 mov -0x8(%rbp),%rax
4004c0: c7 00 03 00 00 00 movl $0x3,(%rax)
4004c6: 5d pop %rbp
4004c7: c3 retq
00000000004004c8 <_Z10function_2Pi>:
4004c8: 55 push %rbp
4004c9: 48 89 e5 mov %rsp,%rbp
4004cc: 48 89 7d f8 mov %rdi,-0x8(%rbp)
4004d0: 48 8b 45 f8 mov -0x8(%rbp),%rax
4004d4: c7 00 03 00 00 00 movl $0x3,(%rax)
4004da: 5d pop %rbp
4004db: c3 retq
The produced assembly code is indeed the same for both functions.
Do they lead to the same result?
No. But in the both cases, x
can be modified by the function. In the first one, you're passing x
by reference. But in the second case, you're passing a pointer to an int
. In the second case all the changes made to the pointer x
will be local, but not the data is changed. That is, consider the following code,
void function_2(int *x)
{
x = new int(3); // This change is local and doesn't affect the variable "arg"
*x = 2;
}
int *arg;
function_2(arg);
cout << x; // OOPS! an error. This is because you're passing the pointer by value.
While this is not working, Zalgo's version of function_2 changes the value of x
, because there the data pointed to by x is changed but in the above code, x
itself is changed. Changing the above code to,
void function_2(int *&x) // Passing a reference to a pointer.
{
x = new int(3); // This change is affects arg,
*x = 2;
}
int *arg;
function_2(arg);
cout << x; // OK.
There are only two types of parameter passing, pass-by-reference and pass-by-value. There is no such thing as pass-by-pointer.
OK, OK, I've got it: passing a reference to a variable instead of passing a pointer is less permissive for the programmer in the function implementation (e.g., you can't modify a reference whereas the pointer can get reassigned within the function block). This is actually the answer to the author's second question, which is:
Is the argument in the first one also passed by reference ?
Now, let's have a look back at the author's first question:
Do they lead to the same result ?
In other terms, assuming we are in a context where both implementation are correct and compile successfuly, what are the differences between one execution and the other ?
The answer is: NONE.
Why ?
Because compilers implement the reference parameter passing in the EXACT SAME WAY than pointer parameters! From an assembly point of view, you just want to push the damn address of the integer (or whatever kind of data) on the top of the function's stack frame. This address eventually gets dereferenced when loading/storing the data. This is the same thing when using references in other declarations.
www.edn.com/Pdf/ViewPdf?contentItemId=4024641 :
The C++ standard is very careful to avoid dictating how a compiler must implement references, but every C++ compiler I've seen implements references as pointers. That is, a declaration such as:
int &ri = i;
if it's not optimized away entirely, allocates the same amount of storage as a pointer, and places the address of i into that storage.
Once again, check the assembly code if you're not convinced:
void function_1(int &x){
x=3;
}
void function_2(int *x){
*x=3;
}
g++ -g -Wall -Werror ptr.c -o ptr
objdump -D ptr > res.txt
00000000004004b4 <_Z10function_1Ri>:
4004b4: 55 push %rbp
4004b5: 48 89 e5 mov %rsp,%rbp
4004b8: 48 89 7d f8 mov %rdi,-0x8(%rbp)
4004bc: 48 8b 45 f8 mov -0x8(%rbp),%rax
4004c0: c7 00 03 00 00 00 movl $0x3,(%rax)
4004c6: 5d pop %rbp
4004c7: c3 retq
00000000004004c8 <_Z10function_2Pi>:
4004c8: 55 push %rbp
4004c9: 48 89 e5 mov %rsp,%rbp
4004cc: 48 89 7d f8 mov %rdi,-0x8(%rbp)
4004d0: 48 8b 45 f8 mov -0x8(%rbp),%rax
4004d4: c7 00 03 00 00 00 movl $0x3,(%rax)
4004da: 5d pop %rbp
4004db: c3 retq
"C++ references" are just syntactic elements introduced by the C++ grammar in order to restrict the manipulations you can do on the address of a variable. At register level, reference = pointer = address.