我只是想知道,如何在内部存储引用?我觉得在这个级别的深层理解会让我更好地理解概念指针与参考,并做出决策选择。
我怀疑它基本上与指针一样,但编译器负责处理指针。请指教。
答案 0 :(得分:14)
不要求以任何方式“存储”引用。就语言而言,引用只是某个现有对象的别名,而这正是任何编译器必须提供的。
完全可能的是,如果引用只是已经在范围内的其他对象的简写,或者带有引用参数的函数被内联,则根本不需要存储任何内容。
在需要明确引用的情况下(例如,在不同的翻译单元中调用函数时),您实际上可以将T & x
实现为T * const
并处理x
的每一次出现1}}隐式取消引用该指针。即使在更高级别,您也可以将T & x = y;
和T * const p = &y;
(以及相应的x
和*p
)视为基本等效,因此这将是实现参考的明显方法
但当然没有要求,任何实施都可以随意做任何事情。
答案 1 :(得分:11)
引用只是内部的别名,编译器将它们视为指针。
但是从使用角度来看,用户存在一些细微差别。
一些主要差异是:
NULL
,而引用则不能。没有任何内容称为NULL
引用。const
引用会延长临时绑定的生命周期。没有与指针等效的内容。此外,引用与const
指针有一些共同点(不是指向const的指针):
当你知道你有什么东西(一个对象)要引用时,你永远不想引用任何其他东西,使用引用,否则使用指针。
答案 2 :(得分:2)
很抱歉使用程序集来解释这一点,但我认为这是了解编译器如何实现引用的最佳方法。
#include <iostream>
using namespace std;
int main()
{
int i = 10;
int *ptrToI = &i;
int &refToI = i;
cout << "i = " << i << "\n";
cout << "&i = " << &i << "\n";
cout << "ptrToI = " << ptrToI << "\n";
cout << "*ptrToI = " << *ptrToI << "\n";
cout << "&ptrToI = " << &ptrToI << "\n";
cout << "refToNum = " << refToI << "\n";
//cout << "*refToNum = " << *refToI << "\n";
cout << "&refToNum = " << &refToI << "\n";
return 0;
}
此代码的输出就像这样
i = 10
&i = 0xbf9e52f8
ptrToI = 0xbf9e52f8
*ptrToI = 10
&ptrToI = 0xbf9e52f4
refToNum = 10
&refToNum = 0xbf9e52f8
让我们看一下反汇编(我为此使用了GDB。这里的8,9和10是代码行号)
8 int i = 10;
0x08048698 <main()+18>: movl $0xa,-0x10(%ebp)
此处$0xa
是我们分配给i
的10(十进制)。 -0x10(%ebp)
此处表示ebp register
-16(十进制)的内容。 -0x10(%ebp)
指向堆栈上i
的地址。
9 int *ptrToI = &i;
0x0804869f <main()+25>: lea -0x10(%ebp),%eax
0x080486a2 <main()+28>: mov %eax,-0x14(%ebp)
将i
的地址指定给ptrToI
。 ptrToI
位于地址-0x14(%ebp)
的堆栈上,即ebp
- 20(十进制)。
10 int &refToI = i;
0x080486a5 <main()+31>: lea -0x10(%ebp),%eax
0x080486a8 <main()+34>: mov %eax,-0xc(%ebp)
现在抓住了!比较第9行和第10行的反汇编,您将观察到行号10中的-0x14(%ebp)
被-0xc(%ebp)
替换。-0xc(%ebp)
是refToNum的地址。它在堆栈上分配。但是,您永远无法从代码中获取此地址,因为您无需知道地址。
因此;引用确实占用了内存。在这种情况下,它是堆栈内存,因为我们已将其分配为局部变量。它占用了多少内存?指针占据了很多。
现在让我们看看我们如何访问引用和指针。为简单起见,我只展示了汇编代码段的一部分
16 cout << "*ptrToI = " << *ptrToI << "\n";
0x08048746 <main()+192>: mov -0x14(%ebp),%eax
0x08048749 <main()+195>: mov (%eax),%ebx
19 cout << "refToNum = " << refToI << "\n";
0x080487b0 <main()+298>: mov -0xc(%ebp),%eax
0x080487b3 <main()+301>: mov (%eax),%ebx
现在比较以上两行,你会看到惊人的相似性。 -0xc(%ebp)
是refToI
的实际地址,您永远无法访问该地址。简单来说,如果您将引用视为普通指针,那么访问引用就像获取引用指向的地址处的值一样。这意味着以下两行代码将为您提供相同的结果
cout << "Value if i = " << *ptrToI << "\n";
cout << " Value if i = " << refToI << "\n";
现在比较一下
15 cout << "ptrToI = " << ptrToI << "\n";
0x08048713 <main()+141>: mov -0x14(%ebp),%ebx
21 cout << "&refToNum = " << &refToI << "\n";
0x080487fb <main()+373>: mov -0xc(%ebp),%eax
我想你能发现这里发生了什么。如果您要求&refToI
,则会返回-0xc(%ebp)
地址位置的内容,-0xc(%ebp)
位于refToi
所在的位置,其内容只是i
的地址。
最后一件事,为什么这一行被评论了?
//cout << "*refToNum = " << *refToI << "\n";
因为不允许*refToI
,它会给你一个编译时错误。