不要忘记编译器只会编写汇编代码。如果您忽略编译器的所有警告,您可以检查编译器生成的汇编代码并找出真正发生的情况。
我采用了这个简单的程序:
#include <iostream>
int main()
{
unsigned endian[2] = { 0, 0 } ;
long * casted_endian = reinterpret_cast<long*>( endian );
std::cout << *casted_endian << std::endl;
}
我使用objdump
. 让我们破译一下。
804879c: 55 push %ebp
804879d: 89 e5 mov %esp,%ebp
804879f: 83 e4 f0 and $0xfffffff0,%esp
80487a2: 83 ec 20 sub $0x20,%esp
这些行只是函数的序言,忽略它们。
unsigned endian[2] = { 0, 0 } ;
80487a5: c7 44 24 14 00 00 00 movl $0x0,0x14(%esp)
80487ac: 00
80487ad: c7 44 24 18 00 00 00 movl $0x0,0x18(%esp)
80487b4: 00
从这两行中,您可以看到 (0x14)%esp 初始化为 0。因此您知道数组endian
在堆栈上,位于寄存器 %ESP(堆栈指针)+ 0x14 中的地址。
long * casted_endian = reinterpret_cast<long*>( endian );
80487b5: 8d 44 24 14 lea 0x14(%esp),%eax
LEA 只是一个算术运算。EAX 现在包含 %ESP+0x14,它是堆栈上数组的地址。
80487b9: 89 44 24 1c mov %eax,0x1c(%esp)
在地址 ESP + 0x1c (这是变量的位置casted_endian
)我们放置 EAX,因此是字节序的第一个字节的地址。
std::cout << *casted_endian << std::endl;
80487bd: 8b 44 24 1c mov 0x1c(%esp),%eax
80487c1: 8b 00 mov (%eax),%eax
80487c3: 89 44 24 04 mov %eax,0x4(%esp)
80487c7: c7 04 24 40 a0 04 08 movl $0x804a040,(%esp)
80487ce: e8 1d fe ff ff call 80485f0 <std::ostream::operator<<(long)@plt>
然后我们使用相关参数准备对 operator << 的调用,而不再进行任何检查。就是这样,程序将不再进行任何检查。变量的类型与机器完全无关。
现在,当读取不在数组中operator<<
的部分时,可能会发生两件事。*casted_endian
它的地址要么在当前映射的内存页中,要么不在。在第一种情况下,operator<<
将阅读该地址的任何内容而不会抱怨。这可能会在屏幕上写一些奇怪的东西。在第二种情况下,您的操作系统会抱怨程序试图读取他无法读取的内容,并引发中断。这就是著名的分段错误。