我知道引用不会占用任何内存,它将指向它所引用的相同内存位置。 例如
int i=10;
int &r = a;
假设i
指向内存位置1000,因此在这种情况下r
也将指向内存位置1000。
但是在C ++中,当我们声明一个变量时,它将在某个位置存储在内存中。
在这种情况下,r
指向某个位置,但它应该作为内部表示存储在内存中的某个位置,仅用于引用。
提前谢谢。
答案 0 :(得分:6)
这是未指明的,并且有充分的理由。真正的答案是:它取决于参考。它可以表示为普通指针,或者根本不存在。
如果您有具有自动存储持续时间的功能本地参考,例如此r
:
void foo()
{
int x[4] = {0, 1, 2, 3};
int &r = x[1];
// more code
}
然后它可能根本不会占用任何空间。编译器只会将r
的所有用法视为x[1]
的别名,并直接访问int
。请注意,此类别名样式引用也可以来自函数内联。
另一方面,如果引用是“持久的”或对其他转换单元(例如数据成员或全局变量)可见,则它必须占用一些空间并存储在某处。在这种情况下,它很可能被表示为一个指针,使用它的代码将被编译为取消引用该指针。
理论上,其他选项也是可能的(例如查找表),但我不认为这些选项可供任何真实编译器使用。
答案 1 :(得分:3)
我知道引用不占用任何内存
不完全是。引用是否具有存储,未指定。它可能会也可能不会。在这个特定的例子中,它不需要存储,因此在典型的实现中,它不使用任何存储。
它将指向它引用的相同内存位置
这听起来像是重言式或只是误解,取决于你所说的“ point ”。引用引用到对象或绑定到对象。您可以将其视为变量名称的别名。变量名也不使用任何内存。
在这种情况下,r指向某个位置,但它应该存储在内存中的某处
它不需要存储在内存中。请考虑以下代码:
int i=10;
int &r = a;
int j = r * 3;
编译器可以将r * 3
解释为i * 3
,就像您首先编写的那样。引用对象的位置在编译时是已知的,因此不需要将地址存储在运行时的内存中。
但是,在其他情况下,可能需要存储。例如:考虑具有外部链接的非内联函数的引用参数。编译函数时无法知道引用的对象,因此必须在运行时将一些信息传递到内存中。
作为引用的内部表示仅使用const指针
这不正确。内部表示可能使用指针,或者它可能使用其他东西,或者它可能不需要使用任何东西。
所以,简明扼要地回答
参考变量存储在哪里
未指明。无处或无处。
答案 2 :(得分:0)
标准说:
未指明引用是否需要存储(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上尝试的任何编译器上的相同代码 - example:
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
另一方面,谈论结构时,情况会变得更加滑溜;这里允许编译器从结构的实际布局中删除引用(如果它能够重构它们实际指向的内容),而对于指针,情况可能会稍微复杂一些(它们的省略会破坏标准布局类)内容)。
实际上,我从未真正看到过在任何实际编译器中实现的这种优化。拿gcc或MSVC或Clang等等,你总会看到结构大小等于even in the most trivial cases。
答案 3 :(得分:0)
作为引用的内部表示仅使用const指针
你在哪里听到的?事实并非如此。
标准没有规定如何实施参考。
以下是过度简化
最常见的是,编译器在内部有一个符号表,它存储了变量所需的所有信息。
让我们来看一个简单的案例:
int a;
a = 100;
然后编译器可以有这样的东西(为简单起见,让a
的地址成为固定的已知地址)
| identifier | type | address |
|------------|------|----------|
| a | int | 0xFF00A4 |
然后它可以将c ++代码转换成这样的代码:
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
如果函数中有引用参数,则会在内部传递指针。