![](/img/trans.png)
[英]What if a reference is declared to point to itself? E.g. int& x = x;
[英]foo(int& x); is x passed by reference?
这是 :
void foo(int& x){
....
}
....
foo(arg);
与此相同:
void foo(int * x){
....
}
....
foo(&arg);
它们会导致相同的结果吗? 第一个中的参数是否也通过引用传递?
&
符号可以与声明中的类型一起使用,例如:
int& x; // #1
或者在标识符之前,例如:
&x // #2
第一个变体意味着您将变量声明为引用 (在本例中是对int
的引用)。
第二种变体意味着您调用address-of运算符,该运算符返回数据的内存地址。
的两个用途&
是完全无关的,并没有任何关系彼此。 他们只是使用相同的标志。
在第一个示例中,您声明了一个函数,该函数通过引用 int
来获取参数( &
在声明类型时使用):
void foo(int& x) {
/* ... */
}
/* ... */
foo(arg); // Pass 'arg' to function.
在你的第二个例子中,你声明一个函数通过指向 int
的参数获取参数,然后在传递它之前使用arg
上的address-of运算符(有效地传递arg
的地址):
void foo(int* x) {
/* ... */
}
/* ... */
foo(&arg); // Pass address of 'arg' to function.
有关:
我认为这是一个很好的问题,我不同意其他人的反应。 在我看来,两个函数都有一个通过引用传递的整数参数(即,变量的地址被推送到被调用的堆栈上)。 使用&符号只是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
生成的汇编代码对于两个函数确实是相同的。
它们会导致相同的结果吗? 不。但在这两种情况下, 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.
参数传递只有两种类型,即传递引用和按值传递。 没有传递指针这样的东西。
好的,好的,我已经知道了:传递对变量的引用而不是传递指针对函数实现中的程序员来说是不太容许的(例如,你不能修改引用,而指针可以在函数内重新赋值)块)。 这实际上是作者第二个问题的答案,即:
第一个中的参数是否也通过引用传递?
现在,让我们回顾一下作者的第一个问题:
它们会导致相同的结果吗?
换句话说,假设我们处于两个实现都正确并且成功编译的上下文中,一个执行与另一个执行之间有什么区别?
答案是:没有。
为什么?
因为编译器实现的参考参数传递的方式与指针参数完全相同! 从汇编的角度来看,您只想在函数堆栈帧的顶部推送整数(或任何类型的数据)的该死的地址。 在加载/存储数据时,该地址最终会被取消引用。 在其他声明中使用引用时,这是相同的。
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
“C ++引用”只是C ++语法引入的语法元素,以限制您可以对变量地址进行的操作。 在寄存器级别,reference = pointer = address。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.