指令集如何区分值和引用

How do instruction sets differentiate value from reference

让我们看看这段代码:

int main ()
{
    int a = 5;
    int&b = a;

    cout << a << endl;  // 5 is displayed
    cout << b << endl;  // 5 is also displayed

    return 0;
}

这是我在调试器中看到的行为。

int a = 5会在内存地址-0x14(%rbp)

中赋值5

int& b = a会在内存地址-0x8(%rbp)

中赋值-0x14(%rbp)

当我执行 cout << a << endl 时,将显示 a 地址中的值(即 -0x14(%rbp))。

但是不知何故,当我执行 cout << b << endl 时,b 地址中的值(即 -0x8(%rbp))被确定为一个地址,那么该地址(-0x14(%rbp))的值是显示。

这是 std::cout 调用的程序集:

20                  cout << a << endl;
0000000000401506:   mov -0xc(%rbp),%eax
0000000000401509:   mov %eax,%edx
000000000040150b:   lea 0x6f8c9c6e(%rip),%rcx        # 0x6fccb180 <libstdc++-6!_ZSt4cout>
0000000000401512:   callq 0x4015f8 <_ZNSolsEi>
0000000000401517:   lea 0xe2(%rip),%rdx        # 0x401600 <_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_>
000000000040151e:   mov %rax,%rcx
0000000000401521:   callq 0x401608 <_ZNSolsEPFRSoS_E>
21                  cout << b << endl;
0000000000401526:   mov -0x8(%rbp),%rax
000000000040152a:   mov (%rax),%eax
000000000040152c:   mov %eax,%edx
000000000040152e:   lea 0x6f8c9c4b(%rip),%rcx        # 0x6fccb180 <libstdc++-6!_ZSt4cout>
0000000000401535:   callq 0x4015f8 <_ZNSolsEi>
000000000040153a:   lea 0xbf(%rip),%rdx        # 0x401600 <_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_>
0000000000401541:   mov %rax,%rcx
0000000000401544:   callq 0x401608 <_ZNSolsEPFRSoS_E>
24                  return 0;

问题:

两个std::cout指令非常相似,ab有何区别?

简而言之:不是。

CPU本身并不关心哪个类型存储在哪里,它只是执行编译器生成的指令。

编译器知道 b 是引用,而不是 int。所以它指示 CPU 将 b 视为指针。

如果您查看程序的汇编代码,您会发现访问 ab 的指令是不同的:b 的部分包含一个额外的说明

mov (%rax),%eax

这是解除引用的步骤。 (在这个汇编符号中,括号表示解引用,所以这个指令的意思类似于 eax = *rax)。

我想您绝对没有要求优化。虽然连 然后,我希望访问 a 并访问 b 来生成 完全相同的代码(在本例中,至少)。

关于编译器如何知道:ab有不同 类型,所以编译器知道用它们做不同的事情。这 标准的设计使得 int& 替换为 int* const, 然后在每次访问时自动解除引用(除了 初始化)将导致一致的实现;它看起来 这就是你的编译器正在做的事情。