指令集如何区分值和引用
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指令非常相似,a
与b
有何区别?
简而言之:不是。
CPU本身并不关心哪个类型存储在哪里,它只是执行编译器生成的指令。
编译器知道 b
是引用,而不是 int
。所以它指示 CPU 将 b
视为指针。
如果您查看程序的汇编代码,您会发现访问 a
和 b
的指令是不同的:b
的部分包含一个额外的说明
mov (%rax),%eax
这是解除引用的步骤。 (在这个汇编符号中,括号表示解引用,所以这个指令的意思类似于 eax = *rax)。
我想您绝对没有要求优化。虽然连
然后,我希望访问 a
并访问 b
来生成
完全相同的代码(在本例中,至少)。
关于编译器如何知道:a
和b
有不同
类型,所以编译器知道用它们做不同的事情。这
标准的设计使得 int&
替换为 int* const
,
然后在每次访问时自动解除引用(除了
初始化)将导致一致的实现;它看起来
这就是你的编译器正在做的事情。
让我们看看这段代码:
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指令非常相似,a
与b
有何区别?
简而言之:不是。
CPU本身并不关心哪个类型存储在哪里,它只是执行编译器生成的指令。
编译器知道 b
是引用,而不是 int
。所以它指示 CPU 将 b
视为指针。
如果您查看程序的汇编代码,您会发现访问 a
和 b
的指令是不同的:b
的部分包含一个额外的说明
mov (%rax),%eax
这是解除引用的步骤。 (在这个汇编符号中,括号表示解引用,所以这个指令的意思类似于 eax = *rax)。
我想您绝对没有要求优化。虽然连
然后,我希望访问 a
并访问 b
来生成
完全相同的代码(在本例中,至少)。
关于编译器如何知道:a
和b
有不同
类型,所以编译器知道用它们做不同的事情。这
标准的设计使得 int&
替换为 int* const
,
然后在每次访问时自动解除引用(除了
初始化)将导致一致的实现;它看起来
这就是你的编译器正在做的事情。