不要忘記,編譯器只會編寫彙編代碼。如果忽略編譯器的所有警告,則可以檢查編譯器生成的彙編代碼,並找出真正發生的事情。
我把這個簡單的程序:
#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
從這些2行,可以看到(0×14)%尤指與初始化爲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<<
將讀取該地址的任何內容而不抱怨。這可能會在屏幕上寫一些奇怪的東西。在第二種情況下,您的操作系統會抱怨程序嘗試讀取他無法讀取的內容,並引發中斷。這是着名的分段故障。
比這樣的演員陣容更多的是排在前面。鑄造目標的基本數據類型很可能也需要內存對齊前提條件。即這是一個非常微妙的方式來分段錯誤,並且只有一些時間。在下雨的星期六總是很有趣地跟蹤這些人。 – WhozCraig