有没有办法找到一个引用的地址?

85

有没有办法找到引用的地址?

更具体地说,是变量本身的地址而不是它初始化的变量的地址。


2
@Sandeep:"变量本身"只是初始化的变量的另一个名称。与指针不同,没有单独的变量。引用和被引用的变量都是同一个变量。 - Agnel Kurian
4
这个案例再次证明了参考文献相当无用。它们基本上只是为那些不会输入“->”而想要键入“.”的人准备的。 - Cosine
1
@AgnelKurian:有时候。然而,你可以将引用作为成员或函数参数,这一事实与该推理相矛盾。 - Mooing Duck
12
@Cosine:什么?完全胡说八道。 - Lightness Races in Orbit
2
@MooingDuck:我仍然认为这是一个抽象泄漏:P - Lightness Races in Orbit
显示剩余2条评论
10个回答

98

引用没有自己的地址。虽然引用可以作为指针来实现,但没有这种需要或保证。

C++ FAQ最好地解释了它:

与指针不同,一旦引用绑定到对象上,就不能将其“重新绑定”到另一个对象上。 引用本身不是对象(它没有标识;获取引用的地址会给您引用对象的地址;请记住:引用就是引用对象)。

还请参见我的答案,以获取有关引用与指针之间区别的全面列表

引用就是引用对象本身


13
你是正确的。但有时使用“别称”这个术语比使用“指称”更容易理解。 - Martin York
1
有没有参考指针的示例实现?我真的很想看看它们是如何实现的。如果你遇到了任何例子,请分享一下。 - Hemanth
1
@Saran:GCC是开源的。 - Lightness Races in Orbit
顺便说一下,这个规则打败了va_start(vargs,a_referece_argument)。 :( - Mordachai
@Mordachai:不是很准确,va_start 的第二个参数如果是引用类型就会导致未定义行为。 - Lightness Races in Orbit
显示剩余3条评论

53

不行。没有办法获取一个引用的地址。
那是因为一个引用不是一个对象,它只是一个别名(也就是说它是另一个对象的名称)。

int  x = 5;
int& y = x;

std::cout << &x << " : " << &y << "\n";

这将打印出相同的地址。
这是因为 'y' 只是对象 'x' 的另一个名称(别名)。


但可以说那就是它的地址。 - Paul Childs
1
@PaulChilds 是的。但不是以OP所问的方式。请看问题。 - Martin York

28

ISO标准说得最好:

不得引用引用,不得使用引用数组和指向引用的指针。

我不太喜欢很多人在这里使用的逻辑,即因为引用“不能保证只是一个指针”,所以你不能这样做。就像 int x 可能只是一个处理器寄存器而没有地址,但是当使用 &x 时,它会神奇地变成一个内存位置,编译器仍然可能允许你想要的东西。

过去,许多编译器确实允许您完全做到这一点,例如:

int x, y;
int &r = x;
&r = &y; // use address as an lvalue; assign a new referent

我刚刚检查了一下,GCC可以编译它,但有一个强烈的警告,并且生成的程序是错误的。


1
哇,我简直不敢相信那曾经被允许过!在什么扩展或标准的模糊定义下,它从未成为一个硬错误?你还记得结果诊断的措辞吗?现在,幸运的是,没有参数的 g++ 会给出 error: lvalue required as left operand of assignment - underscore_d
@underscore_d 这总是不好的,与引用精神相违背。我不知道它是否曾经是一个旨在实现的特性,还是编译器内部的一个泄漏抽象,它忘记了防止对内部指针进行左值访问。 - Potatoswatter
但是那个程序说“将地址分配给地址”。然而,&x 作为左值也是无效的。引用只是 x 的别名。改变一个地址有什么意义呢?地址就是一种存在的东西。它不可分配。或者最多,它是 x 地址的具体化临时副本,可以分配,但不会影响 x 的地址。 - v.oddou

14

在TC++PL中,Bjarne Stroustrup说,引用可以被视为对现有实体(对象或函数)的另一个名称。虽然这并不总是实现引用的底层机制的最精确描述,但它是引用在语言级别上旨在实现的概念的非常好的描述。不出所料,语言没有提供任何手段来获取引用本身的地址。

在语言层面上,引用不能保证占据存储空间,并且因此在一般情况下它没有地址。


9

只需使用'&'运算符即可。 例如:

int x = 3;
int &y = x;
cout<<&y<<endl;

这将返回 x 的地址,因为 y 不过是 x 的地址。

3
我认为Sandeep的意思是“参考对象本身的地址”。 - Drew Dormann
1
这将有效地打印出x的地址(因为y作为引用只是x的另一个名称)。 - Evan Teran
5
好的,一个引用只是一个对象的别名(另一种名称)。它不会获得自己的内存单元。 - Jesse Emond
5
@Sandeep说:"指针有自己的地址"。正确。这是因为指针是一个独立的变量,保存着它所指向变量的地址。对于引用来说,没有独立的变量。引用只是被引用变量的另一个名称。对引用取地址将返回被引用变量的地址。 - Agnel Kurian
@rafak 有两个反驳意见:(a) 如果实际上不需要引用,即变量所在的作用域与引用相同,而引用仅用于方便或类似情况,则编译器可以看到并优化掉间接寻址;此外,“相同作用域”在使用 LTO 时的含义越来越不传统。(b) 如果实际上需要引用,例如因为在编译时不知道真正的被引用变量或其他原因,则是必需的,因此无需担心间接寻址的成本,因为您无法避免它。 :P - underscore_d
显示剩余3条评论

9
从同样的问题的另一个实例中得知:$8.3.2/3 - "未指定引用是否需要存储(3.7)"。
因此,C++标准允许编译器/运行时实现者选择是否在单独的内存位置中存储引用。但请注意,如果它确实存在于单独的内存位置中,则无法以符合标准的方式找到其地址。所以不要这样做。
如果您获取引用的地址,在C++标准中,根据定义,您将获得它所引用内容的地址,而不是引用的地址,即使引用作为单独的实体在运行时环境中不存在。

4

不能保证可靠性,因为引用不一定具有唯一的可寻址内存位置。


3

单独使用是不行的。如果您想要它的“地址”,请将其放入结构体或类中。即使这样,也不能保证能让您接近您可能想要使用指针的目的。如果您需要证明,请尝试使用char &并查看引用的大小。


1

这是可能的,但不严格使用C++。由于引用作为函数参数传递,其值将存储在堆栈或寄存器中。这取决于硬件架构。 访问这些值需要内联汇编。请查阅您正在使用的处理器的参考手册以确定堆栈行为和寄存器地址。破坏堆栈或寄存器可能会导致蓝屏、数据丢失甚至永久损坏您的系统。请极度小心操作。


0
如果将引用作为结构体的成员实现,就可以获取其地址:
struct TestRef{
  int& r;
  int i;
  TestRef(int& ref): r(ref){
  }
};

引用实际上是一个指针(在我的情况下使用Xcode编译器),您可以更新其值以将引用重新分配给新变量。为此,我们需要找到引用的地址,并将其值欺骗为其他变量的地址。

现在,引用TestRef.r的地址是TestRef对象的地址,因为r是TestRef的第一个成员。

您可以通过更新存储在TestRef.r内存中的值来重新分配引用。

以下代码显示了您可以获取引用的地址并将引用重新分配给不同变量。注意:我的操作系统是X64操作系统(我使用的是Xcode MacBook Pro 2015,MacOs 10.15.1)。

#include <iostream>
using namespace std;

struct TestRef{
  int& r;
  int i;
  TestRef(int& ref): r(ref){}
};

int main(int argc, const char * argv[]) {
  int i = 10;
  int j = 11;
  TestRef r(i); // r.r is reference to i

  cout << r.r << " " << i << " " << j << endl; // Output: 10 10 11

  int64_t* p = (int64_t*)&r; // int32_t in 32 bit OS; 
  // Note: 
  // p is the address of TestRef r and also the address of the reference r.r
  // *p is the address of i variable
  //
  // Difficult to understand? r.r indeed a pointer to i variable 
  // *p will return the address inside the memory of r.r
  // that is the address of i variable
  // this statement is true: *p == &i  
  // ------>
  // now we change the value of *p to the address of j 
  // then r.r will be the reference of j instead the reference of i

  *p = (int64_t)&j;          // int32_t in 32 bit OS; 

  cout << r.r << " " << i << " " << j << endl; // Output: 11 10 11
  return 0;
}

实际上,你可以像黑客一样绕过重新分配引用的限制。


请注意,construct_at 可用于重新分配引用(在其位置上透明地创建一个新的引用,由现有名称访问),无需使用 hackish 技巧。C++20 提交了一个缺陷报告,称使用 placement-new 进行此操作实际上一直是合法的。 - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接