这是:
void foo(int& x){
....
}
....
foo(arg);
与此相同:
void foo(int * x){
....
}
....
foo(&arg);
他们会导致相同的结果吗?第一个中的参数是否也通过引用传递?
答案 0 :(得分:3)
&
符号可以在声明中与类型一起使用,例如像这样:
int& x; // #1
或标识符之前,例如像这样:
&x // #2
第一种变体意味着您将变量声明为引用(在本例中是对int
的引用)。
第二种变体意味着您调用 address-of 运算符,该运算符返回数据的内存地址。
&
的两种用法完全不相关,并且没有彼此相关。他们只是使用相同的标志。
在您的第一个示例中,您声明一个函数将参数引用带到int
(声明类型时使用的&
):
void foo(int& x) {
/* ... */
}
/* ... */
foo(arg); // Pass 'arg' to function.
在你的第二个例子中,你声明一个函数将指针的参数带到int
,然后在{{1}上使用 address-of 运算符。在传递它之前(有效地传递arg
的地址):
arg
相关:强>
答案 1 :(得分:0)
我认为这是一个很好的问题,我不同意其他人的反应。 在我看来,两个函数都有一个通过引用传递的整数参数(即,变量的地址被推送到被调用的堆栈上)。使用&符号只是C ++语法糖,C标准中没有。
为了说服您这两种语法具有完全相同的行为,您可以编译和反汇编以下代码:
void function_1(int &x){
x=3;
}
void function_2(int *x){
*x=3;
}
int main(void){
return 0;
}
使用
进行编译和反汇编后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
两个函数的生成汇编代码确实相同。
答案 2 :(得分:0)
他们会导致相同的结果吗?
不。但在这两种情况下,x
都可以通过函数进行修改。在第一个中,您通过引用传递x
。但在第二种情况下,您将指针传递给int
。在第二种情况下,对指针x
所做的所有更改都将是本地的,但数据不会更改。也就是说,请考虑以下代码,
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.
虽然这不起作用,但Zalgo的function_2版本更改了x
的值,因为x指向的数据已更改,但在上面的代码中,x
本身是改变。将上面的代码更改为,
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.
只有两种类型的参数传递,传递引用和传值。没有像指针那样的东西。
答案 3 :(得分:0)
好的,好的,我已经知道了:传递对变量的引用而不是传递指针对函数实现中的程序员来说是不太容许的(例如,你不能修改引用,而不是指针可以在功能块内重新分配)。这实际上是作者第二个问题的答案,即:
第一个参数是否也通过引用传递?
现在,让我们回顾一下作者的第一个问题:
他们会导致相同的结果吗?
换句话说,假设我们处于两个实现都正确并且成功编译的上下文中,一个执行与另一个执行之间有什么区别?
答案是:没有。
为什么?
因为编译器实现的引用参数传递的方式与指针参数完全相同!从汇编的角度来看,您只想在函数堆栈框架的顶部推送整数(或任何类型的数据)的该死的地址。在加载/存储数据时,该地址最终会被取消引用。在其他声明中使用引用时也是如此。
www.edn.com/Pdf/ViewPdf?contentItemId=4024641:
C ++标准非常小心,以避免规定编译器的方式 必须实现引用,但我见过的每个C ++编译器都实现了 引用作为指针。也就是说,声明如:
int &ri = i;
如果它没有完全优化,则分配与指针相同的存储量,并将i的地址放入该存储器中。
再次检查汇编代码,如果您不相信:
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
&#34; C ++参考&#34;只是由C ++语法引入的语法元素,以限制您可以对变量的地址进行的操作。 在寄存器级别,reference = pointer = address。