我知道引用不会占用任何内存,它将指向与其引用的内存位置相同的位置。 例如:
int i=10;
int &r = a;
假设
i
指向内存位置1000,那么在这种情况下,r
也将指向内存位置1000。
但在C++中,每当我们声明一个变量时,它都会被存储在某个内存位置。
在这种情况下,r
指向某个位置,但应该通过某种内部表示方式存储在内存中。
提前致谢。我知道引用不会占用任何内存,它将指向与其引用的内存位置相同的位置。 例如:
int i=10;
int &r = a;
i
指向内存位置1000,那么在这种情况下,r
也将指向内存位置1000。
但在C++中,每当我们声明一个变量时,它都会被存储在某个内存位置。
在这种情况下,r
指向某个位置,但应该通过某种内部表示方式存储在内存中。
提前致谢。这一点没有具体说明,但是有很好的理由。真正的答案是:它取决于引用的情况。它可以表示为普通指针,或者根本不存在。
如果您有一个具有自动存储期限制的函数本地引用,例如此r
:
void foo()
{
int x[4] = {0, 1, 2, 3};
int &r = x[1];
// more code
}
如果引用不是“持久的”或对其他翻译单元可见(例如数据成员或全局变量),那么它可能根本不占用任何空间。编译器将简单地将r
的所有使用视为x[1]
的别名,并直接访问该int
。请注意,这种别名样式的引用也可能来自函数内联。
另一方面,如果引用是“持久的”或对其他翻译单元可见(例如数据成员或全局变量),则必须占用一些空间并存储在某个位置。在这种情况下,它很可能被表示为指针,并且使用它的代码将被编译为解引用该指针。
理论上,其他选项也可能存在(例如查找表),但我认为这些选项不会被任何现实世界的编译器所使用。
我知道引用不占用任何内存。
并不完全正确。一个引用是否有存储空间是未指定的。它可能有,也可能没有。在这个特定的例子中,它不需要存储空间,因此在典型的实现中,它不使用任何存储空间。
它将指向与其引用相同的内存位置
这听起来像是自我证明或者只是一种误解,具体取决于您对“指向”一词的理解。引用“引用”对象或者“绑定”到对象。您可以将其视为变量名称的别名。变量名称也不使用任何内存。
在这种情况下,r指向某个位置,但它应该被存储在内存中
它不需要被存储在内存中。请考虑以下代码:
int i=10;
int &r = a;
int j = r * 3;
r * 3
解释为i * 3
,就好像你一开始就这样写的一样。所引用对象的位置在编译时已知,因此无需在运行时存储地址。这是不正确的。内部表示可能使用指针,也可能使用其他内容,或者可能根本不需要使用任何东西。在引用使用内部表示时只使用const指针
这是未指定的。可能是没有地方,也可能是某个地方。引用变量存储在哪里?
在引用的内部表示中只使用const指针
你从哪里听到这个的?那是不正确的。
标准没有规定如何实现引用。
以下是过于简化的说明
最常见的情况是,编译器在内部有一个符号表,其中存储了所有关于变量的信息。
我们来看一个简单的例子:
int a;
a = 100;
a
的地址成为已知的固定地址),那么就可以得到以下结果。| identifier | type | address |
|------------|------|----------|
| a | int | 0xFF00A4 |
mov 0xFF00A4, 100
int a;
a = 100;
int& ra = 300;
| identifier | type | address |
|------------|------|----------|
| a | int | 0xFF00A4 |
| ra | int& | 0xFF00A4 |
或者:
| identifier | type | address | alias |
|------------|------|----------|-------|
| a | int | 0xFF00A4 | - |
| ra | int& | - | a |
mov 0xFF00A4, 100
mov 0xFF00A4, 300
标准规定:
未指定引用是否需要存储(3.7)。
(C++11,[dcl.ref] ¶4)
这意味着编译器可以根据情况自由选择是否需要任何存储。
现在,让人们说什么都可以,但引用归结为指针的语法糖(即使在编译器级别,在所有主要的 C++ 编译器中,“引用”概念几乎在前端之后立即消失)。因此,在一般情况下,它们可能需要在内存中占用空间,就像指针一样。但是,在像您这样的情况下(局部引用),编译器应该能够了解它们并根据需要对其进行优化。
请注意,这并不是引用的专属 - 编译器甚至可以通过指针执行相同类型的优化(一旦您的代码进入 SSA 形式,引用不能重新设置也没有什么特别的地方)。
这个:
int glob;
void direct() {
glob = 16;
}
void through_reference() {
int &a = glob;
a = 16;
}
void through_pointer() {
int *a = &glob;
*a = 16;
}
归根结底,在我尝试过的任何编译器上,代码始终相同,在gcc.godbolt.org上也是如此- 示例:
direct():
mov DWORD PTR glob[rip], 16
ret
through_reference():
mov DWORD PTR glob[rip], 16
ret
through_pointer():
mov DWORD PTR glob[rip], 16
ret
glob:
.zero 4