2.2 I386平臺的內存佈局
衆所周知,I386是32位體系結構。因此對於絕大多數I386平臺的C++編譯器而言,sizeof(int)=sizeof(long)=sizeof(void*)=4。當然C++標準對此沒有任何保證,我們也不應該試圖編寫依賴於此的代碼。
1 代碼及靜態數據區
由代碼加載器從動態鏈接庫鏡像(通常是exe或dll文件)加載,通常定位到鏡像文件中指定的基址開始的內存區。如果基址所在內存已被佔用,動態連接器會將代碼或數據重定向到其它可用地址。
2 棧(Stack)
棧是最常用的動態數據存儲區,所有函數的non-static對象和函數參數都在程序運行期在棧上分配內存。在數據結構中,術語“棧(Stack)”意指先進後出(FILO,First In Last Out),與“隊列(Queue)”所指的FIFO相對。相對於基於堆的對象分配技術,默認使用棧的對象分配有兩點優勢:
一、棧的FILO與人的思維方式相同
現實生活中有許多事例都使用FILO的方式,比如人們必須先提起話筒再撥打號碼,而後掛斷電話之後再放下話筒。使用FILO的棧,可以保證事物的銷燬順序以其誕生順序相反的順序進行,不會產生在掛斷電話之前就放下話筒的尷尬。
二、棧的分配管理僅需要兩個額外指針:棧頂(esp)和棧底(ebp)指針
從實現的技術層面而言,棧的管理比其它動態分配技術要簡單很多。I386平臺上的動態棧管理,僅需要棧頂和棧底兩個指針。這兩個指針的存儲顯然不能放置於棧中,置於靜態數據區又有損效率。I386平臺爲管理動態棧專門預留了兩個通用寄存器變量esp與ebp,分別代表棧頂(esp,Extended Stack Pointer)與棧底(Extended Bottom Pointer)指針。其中的extended代表它們是32位指針,以區分16位的sp和bp寄存器。
3 堆(Heap)和自由存儲區
棧中的變量對於分配與釋放的順序有特定要求,這在一定程度上限制了棧的適用範圍。面向對象(OO,Object Oriented)的程序設計思想也要求能自由地控制變量的分配與銷燬。由此,現代操作系統都提供了被稱作“堆(Heap)”的自由存儲區,以允許由程序員控制的對象創建和銷燬過程。C標準庫函數malloc和free則是對操作系統提供的堆操作的封裝。C++提供的自由存儲區運算符new和delete則通常是malloc和free的又一層封裝。
操作系統經由malloc和free控制對堆的訪問。堆的存儲管理技術各不相同,簡單的使用雙鏈表管理,複雜的可以比擬一個完整的文件系統。
由於額外的管理需求,使用系統提供的通用分配器在堆上分配和銷燬變量的代價,無論從空間角度還是效率角度而言,都比在棧上分配對象要高昂很多。對於sizeof上百的大型對象,這樣的高昂代價還是可以接受的,但是對於sizeof只有個位數的小對象,這樣的代價通常是一個數量級的差距。正因爲這個原因,STL不使用new和delete,轉而使用分配子(alllocor)分配對象。
這篇文章是 "CPP_Tricks" 系列文章的第 5 篇:
- C++ Tricks
- C++ Tricks 1.1 條件運算符(?:)
- C++ Tricks 1.2 逗號運算符(,)、邏輯運算符(&&,||)與運算符重載的陷阱
- C++ Tricks 2.1 X86概述
- C++ Tricks 2.2 I386平臺的內存佈局
- C++ Tricks 2.3 I386平臺C函數內部的棧分配
- C++ Tricks 2.4 I386平臺C函數調用邊界的棧分配
- C++ Tricks 2.5 I386平臺的邊界對齊(Align)
- C++ Tricks 2.6 I386平臺C函數的可變參數表(Variable Arguments)
- C++ Tricks 2.7 I386平臺的其它函數調用模型
- C++ Tricks 3.1 左值右值與常量性(lvalue,rvalue & constant)
- C++ Tricks 3.2 標號、goto,以及switch的實現
Github Issue 留言
Disqus 留言