【问题标题】:How references are internally stored in c++? [duplicate]引用如何在 C++ 内部存储? [复制]
【发布时间】:2012-03-09 16:16:41
【问题描述】:

我只是想知道,引用是如何在内部存储的?我觉得深入了解该级别将使我更好地理解概念指针与参考并做出决策选择。

我怀疑它基本上与指针一样工作,但编译器负责处理指针。请指教。

【问题讨论】:

  • “我觉得深入了解该级别将使我更好地理解概念指针与引用”我认为这不会有任何帮助。
  • @MR.Anubis..... 你是什么意思?
  • 指针和引用是访问远程/不同内存位置的一种方式。使用指针,这是明确的。你知道你指向的东西,因此必须小心。通过引用您,编译器会为您处理内部结构。所以指针->显式引用->隐式。两者之间还有很多不同之处,但这是基本区别。

标签: c++ pointers reference


【解决方案1】:

根本不要求以任何方式“存储”引用。就语言而言,引用只是某个现有对象的别名,这就是任何编译器必须提供的全部内容。

如果引用只是范围内其他对象的简写,或者带有引用参数的函数被内联,则完全有可能根本不需要存储任何内容。

在需要明确引用的情况下(例如,在不同的翻译单元中调用函数时),您实际上可以将T & x 实现为T * const,并将每次出现的x 视为隐式取消引用那个指针。即使在更高的层次上,您也可以将T & x = y;T * const p = &y;(以及相应的x*p)视为基本等价的,因此这将是实现引用的明显方式。

但当然没有任何要求,任何实现都可以随意做任何事情。

【讨论】:

  • 我明白了,从您的帖子中,我想您是在说,对于使用引用,不需要分配任何额外的变量,您只需将名称添加到您正在维护的变量表中(虽然这是我的假设)。我不知道,现在 c++ 是如何工作的,但是如果我实现一种新的 oop 语言,我仍然可以确保在内存中没有分配额外的变量,只要记住变量 'a' 有另一个名字叫做“a_ref”??? ?我做对了吗?
  • @howtechstuffworks:只要引用只引用本地或成员范围变量,那么可以。
【解决方案2】:

引用只是内部的别名,编译器将它们视为指针。

但对于用户而言,从使用角度来看,存在一些细微的差别。

一些主要区别是:

  • 指针可以是NULL,而引用不能。没有任何东西称为NULL 引用。
  • const 引用延长了临时绑定到它的生命周期。没有指针的等价物。

此外,引用与const指针(不是指向const的指针)有一些共同点:

  • 引用必须在创建时初始化。
  • 引用永久绑定到单个存储位置,以后无法重新绑定。

当你知道你有东西(一个对象)要引用并且你永远不想引用其他任何东西时,使用引用,否则使用指针。

【讨论】:

  • @SigTerm 如果你仔细瞄准,你可能会踢自己的脚。
  • @SigTerm 无论如何这都是未定义的行为。
  • @SigTerm 我没有什么要证明的,只是去阅读标准。问题出在*((int*)0):您正在取消引用一个空指针。你很幸运,它没有立即崩溃。如果它没有崩溃,那么是的,你总是可以将它绑定到一个引用,但这并不会改变首先取消引用指针是 UB 的事实。
  • @SigTerm 我认为我实际上不必为如此明显的事情引用标准。
  • @SigTerm:请停止争论一个不存在的愚蠢问题。您添加了一个根本不正确的论点,并且没有用户告诉您它不正确的原因,如果您还想参加比赛它的有效性你应该从标准中拿出一个反引号来证明你的观点,如果不是,你应该接受错误从中吸取教训并继续前进,IMO 这应该是在这里的全部目的,在努力学习和提高自己的同时帮助别人。为争论而争论是没有用的。请长大,或者如果你已经长大了,请改变态度。
【解决方案3】:

很抱歉使用汇编来解释这一点,但我认为这是了解编译器如何实现引用的最佳方式。

#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的地址分配给ptrToIptrToI 再次位于地址-0x14(%ebp) 的堆栈上,即ebp – 20(十进制)。

10          int &refToI = i;
0x080486a5 <main()+31>: lea    -0x10(%ebp),%eax
0x080486a8 <main()+34>: mov    %eax,-0xc(%ebp)

现在问题来了!比较第 9 行和第 10 行的反汇编,你会发现,-0x14(%ebp) 在第 10 行被替换为-0xc(%ebp)-0xc(%ebp) 是 refToNum 的地址。它在堆栈上分配。但是你永远无法从你的代码中得到这个地址,因为你不需要知道这个地址。

所以;引用确实占用内存。在这种情况下,它是堆栈内存,因为我们已将其分配为局部变量。它占用多少内存?一个指针占用多少空间。

现在让我们看看我们如何访问引用和指针。为简单起见,我只展示了程序集 sn-p 的一部分

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

我想你能够发现这里发生了什么。如果你要求&amp;refToI,则返回-0xc(%ebp)地址位置的内容,-0xc(%ebp)refToi所在的位置,其内容只是i的地址。

最后一件事,为什么要评论这一行?

//cout << "*refToNum = " << *refToI << "\n";

因为*refToI 是不允许的,它会给你一个编译时错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-05
    • 1970-01-01
    • 2017-08-14
    • 1970-01-01
    • 2021-01-08
    • 2012-09-21
    • 2021-04-03
    相关资源
    最近更新 更多