Yuv*_*dam 42 c++ memory-management reference
鉴于:
int i = 42;
int j = 43;
int k = 44;
Run Code Online (Sandbox Code Playgroud)
通过查看变量地址,我们知道每个地址占用4个字节(在大多数平台上).
但考虑到:
int i = 42;
int& j = i;
int k = 44;
Run Code Online (Sandbox Code Playgroud)
我们将看到,变量i确实需要4个字节,但j需要没有和k再次发生堆栈上的4个字节.
这里发生了什么?看起来j在运行时根本就不存在.那么我作为函数参数收到的引用呢?这必须在堆栈上占用一些空间......
虽然我们在这里 - 为什么我不能定义数组或引用?
int&[] arr = new int&[SIZE]; // compiler error! array of references is illegal
Run Code Online (Sandbox Code Playgroud)
Ara*_*raK 49
在遇到引用j的任何地方,它都被替换为i的地址.所以基本上参考内容地址在编译时被解析,并且不需要像运行时的指针一样取消引用它.
只是为了澄清我的意思是我的地址:
void function(int& x)
{
x = 10;
}
int main()
{
int i = 5;
int& j = i;
function(j);
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码,Ĵ不应该采取的上空间主堆叠,但参考X的函数将其堆栈上的一个地方.这意味着当使用j作为参数调用函数时,将在函数堆栈上推送的i的地址.编译器可以而且不应该在主堆栈上为j保留空间.
对于阵列部分标准说::
C++标准8.3.2/4:
不应引用引用,不引用引用数组,也不引用引用指针.
jal*_*alf 38
内存方面的C++引用如何?
它没有.C++标准只说明它应该如何表现,而不是它应该如何实现.
在一般情况下,编译器通常将引用实现为指针.但是它们通常有更多关于引用可能指向的信息,并将其用于优化.
请记住,引用的唯一要求是它表现为引用对象的别名.因此,如果编译器遇到此代码:
int i = 42;
int& j = i;
int k = 44;
Run Code Online (Sandbox Code Playgroud)
它看到的不是"创建指向变量的指针i"(尽管这是编译器在某些情况下可能选择实现它的方式),而是"在符号表中做一个j现在是别名的注释i".
编译器不必为其创建新变量j,只需要记住,j从现在开始引用时,它应该真正交换出来并使用i.
至于创建一个引用数组,你不能这样做,因为它没用,毫无意义.
创建数组时,所有元素都是默认构造的.默认构造引用意味着什么?它指向什么?引用中的重点是它们被初始化为引用另一个对象,之后它们无法重新定位.
因此,如果可以完成,您最终会得到一系列对任何内容的引用.并且你无法改变它们以引用某些东西,因为它们已经被初始化了.
Pra*_*ane 11
很抱歉使用汇编来解释这一点,但我认为这是了解参考资料的最佳方式.
#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 << "refToI = " << refToI << "\n";
//cout << "*refToI = " << *refToI << "\n";
cout << "&refToI = " << &refToI << "\n";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这段代码的输出是这样的
i = 10
&i = 0xbf9e52f8
ptrToI = 0xbf9e52f8
*ptrToI = 10
&ptrToI = 0xbf9e52f4
refToI = 10
&refToI = 0xbf9e52f8
Run Code Online (Sandbox Code Playgroud)
让我们看看反汇编(我为此使用了GDB.这里是8,9和10是代码的行号)
8 int i = 10;
0x08048698 <main()+18>: movl $0xa,-0x10(%ebp)
Run Code Online (Sandbox Code Playgroud)
这$0xa是我们分配给的10(十进制)i.-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)
Run Code Online (Sandbox Code Playgroud)
指定的地址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)
Run Code Online (Sandbox Code Playgroud)
现在抓住了!比较第9行和第10行的反汇编,您将观察到,在行号10中被-0x14(%ebp)替换为地址.它在堆栈上分配.但是您永远无法从代码中获取此地址,因为您无需知道地址.-0xc(%ebp)-0xc(%ebp)refToI
所以; 引用确实占用了内存.在这种情况下,它是堆栈内存,因为我们已将其分配为局部变量.它占用了多少内存?指针占据了很多.
现在让我们看看我们如何访问引用和指针.为简单起见,我只展示了部分组装片段
16 cout << "*ptrToI = " << *ptrToI << "\n";
0x08048746 <main()+192>: mov -0x14(%ebp),%eax
0x08048749 <main()+195>: mov (%eax),%ebx
19 cout << "refToI = " << refToI << "\n";
0x080487b0 <main()+298>: mov -0xc(%ebp),%eax
0x080487b3 <main()+301>: mov (%eax),%ebx
Run Code Online (Sandbox Code Playgroud)
现在比较以上两行,你会看到惊人的相似性. -0xc(%ebp)是refToI您永远无法访问的实际地址.简单来说,如果您将引用视为普通指针,那么访问引用就像获取引用指向的地址处的值一样.这意味着以下两行代码将为您提供相同的结果
cout << "Value if i = " << *ptrToI << "\n";
cout << " Value if i = " << refToI << "\n";
Run Code Online (Sandbox Code Playgroud)
现在比较一下
15 cout << "ptrToI = " << ptrToI << "\n";
0x08048713 <main()+141>: mov -0x14(%ebp),%ebx
21 cout << "&refToI = " << &refToI << "\n";
0x080487fb <main()+373>: mov -0xc(%ebp),%eax
Run Code Online (Sandbox Code Playgroud)
我猜你能发现这里发生了什么.如果您要求&refToI,-0xc(%ebp)地址位置的内容将 被返回,并且-0xc(%ebp)位于其中refToi,其内容只是地址i.
最后一件事,为什么这一行被评论了?
//cout << "*refToI = " << *refToI << "\n";
Run Code Online (Sandbox Code Playgroud)
因为*refToI不允许,它会给你一个编译时错误.
您无法定义引用数组,因为没有语法来初始化它们.C++不允许未初始化的引用.至于你的第一个问题,编译器没有义务为不必要的变量分配空间.没有办法让j指向另一个变量,因此它实际上只是函数范围中i的别名,而这就是编译器对待它的方式.
在其他地方只提到的东西 - 如何让编译器将一些存储空间用于引用:
class HasRef
{
int &r;
public:
HasRef(int &n)
: r(n) { }
};
Run Code Online (Sandbox Code Playgroud)
这使得编译器无法简单地将其视为编译时别名(同一存储的替代名称).
| 归档时间: |
|
| 查看次数: |
18396 次 |
| 最近记录: |