[转]C++结构体|类 内存对齐详解
?內存地址對齊,是一種在計算機內存中排列數據(表現為變量的地址)、訪問數據(表現為CPU讀取數據)的一種方式,包含了兩種相互獨立又相互關聯的部分:基本數據對齊和結構體數據對齊 。
? ? ? ?為什么需要內存對齊?對齊有什么好處?是我們程序員來手動做內存對齊呢?還是編譯器在進行自動優化的時候完成這項工作?
? ? ? ?在現代計算機體系中,每次讀寫內存中數據,都是按字(word,4個字節,對于X86架構,系統是32位,數據總線和地址總線的寬度都是32位,所以最大的尋址空間為232 = 4GB(也 許有人會問,我的32位XP用不了4GB內存,關于這個不在本篇博文討論范圍),按A[31,30…2,1,0]這樣排列,但是請注意為了CPU每次讀寫 4個字節尋址,A[0]和A[1]兩位是不參與尋址計算的。)為一個塊(chunks)來操作(而對于X64則是8個字節為一個快)。注意,這里說的 CPU每次讀取的規則,并不是變量在內存中地址對齊規則。既然是這樣的,如果變量在內存中存儲的時候也按照這樣的對齊規則,就可以加快CPU讀寫內存的速 度,當然也就提高了整個程序的性能,并且性能提升是客觀,雖然當今的CPU的處理數據速度(是指邏輯運算等,不包括取址)遠比內存訪問的速度快,程序的執 行速度的瓶頸往往不是CPU的處理速度不夠,而是內存訪問的延遲,雖然當今CPU中加入了高速緩存用來掩蓋內存訪問的延遲,但是如果高密集的內存訪問,一 種延遲是無可避免的,內存地址對齊會給程序帶來了很大的性能提升。
? ? ? ?內存地址對齊是計算機語言自動進行的,也即是編譯器所做的工作。但這不意味著我們程序員不需要做任何事情,因為如果我們能夠遵循某些規則,可以讓編譯器做得更好,畢竟編譯器不是萬能的。
? ? ? ?為了更好理解上面的意思,這里給出一個示例。在32位系統中,假如一個int變量在內存中的地址是0x00ff42c3,因為int是占用4個字節,所以它的尾地址應該是0x00ff42c6,這個時候CPU為了讀取這個int變量的值,就需要先后讀取兩個word大小的塊,分別是0x00ff42c0~0x00ff42c3和0x00ff42c4~0x00ff42c7,然后通過移位等一系列的操作來得到,在這個計算的過程中還有可能引起一些總線數據錯誤的。但是如果編譯器對變量地址進行了對齊,比如放在0x00ff42c0,CPU就只需要一次就可以讀取到,這樣的話就加快讀取效率。
? ? ? ?1、基本數據對齊
? ? ? ? ? ? ? ? ?在X86,32位系統下基于Microsoft、Borland和GNU的編譯器,有如下數據對齊規則:
? ? ? ? ? ? ? ? ?a、一個char(占用1-byte)變量以1-byte對齊。
? ? ? ? ? ? ? ? ?b、一個short(占用2-byte)變量以2-byte對齊。
? ? ? ? ? ? ? ? ?c、一個int(占用4-byte)變量以4-byte對齊。
? ? ? ? ? ? ? ? ?d、一個long(占用4-byte)變量以4-byte對齊。
? ? ? ? ? ? ? ? ?e、一個float(占用4-byte)變量以4-byte對齊。
? ? ? ? ? ? ? ? ?f、一個double(占用8-byte)變量以8-byte對齊。
? ? ? ? ? ? ? ? ?g、一個long double(占用12-byte)變量以4-byte對齊。
? ? ? ? ? ? ? ? ?h、任何pointer(占用4-byte)變量以4-byte對齊。
? ? ? ? ? ? ? ? 而在64位系統下,與上面規則對比有如下不同:
? ? ? ? ? ? ? ? ?a、一個long(占用8-byte)變量以8-byte對齊。
? ? ? ? ? ? ? ? ?b、一個double(占用8-byte)變量以8-byte對齊。
? ? ? ? ? ? ? ? ?c、一個long double(占用16-byte)變量以16-byte對齊。
? ? ? ? ? ? ? ? ?d、任何pointer(占用8-byte)變量以8-byte對齊。
? ? ? ?2、結構體數據對齊
? ? ? ?結構體數據對齊,是指結構體內的各個數據對齊。在結構體中的第一個成員的首地址等于整個結構體的變量的首地址,而后的成員的地址隨著它聲明的順序和實際占用的字節數遞增。為了總的結構體大小對齊,會在結構體中插入一些沒有實際意思的字符來填充(padding)結構體。
? ? ? ?在結構體中,成員數據對齊滿足以下規則:
? ? ? ? a、結構體中的第一個成員的首地址也即是結構體變量的首地址。
? ? ? ? b、結構體中的每一個成員的首地址相對于結構體的首地址的偏移量(offset)是該成員數據類型大小的整數倍。
? ? ? ? c、結構體的總大小是對齊模數(對齊模數等于#pragma pack(n)所指定的n與結構體中最大數據類型的成員大小的最小值)的整數倍。
?
? ?7: ?struct
? ?8: ?{
? ?9: ? ? ?char a;
? 10: ? ? ?int b;
? 11: ? ? ?short c;
? 12: ? ? ?char d;
? 13: ?}dataAlign;
? 14: ??
? 15: ?struct
? 16: ?{
? 17: ? ? ?char a;
? 18: ? ? ?char d;
? 19: ? ? ?short c;
? 20: ? ? ?int b;
? 21: ? ? ?
? 22: ?}dataAlign2;
? ? ? ?仔細觀察,會發現雖然是一樣的數據類型的成員,只不過聲明的順序不同,結構體占用的大小也不同,一個8-byte一個12-byte。為什么這樣,下面進行具體分析。 ?
? ? ? ?首先來看dataAlign2,第一個成員的地址等于結構體變量的首地址,第二個成員char類型,為了滿足規則b,它相對于結構體的首地址的偏移量必須 是char=1的倍數,由于前面也是char,故不需要在第一個和第一個成員之間填充,直接滿足條件。第三個成員short=2如果要滿足規則b,也不需 要填充,因為它的偏移量已經是2。同樣第四個也因為偏移量int=4,不需要填充,這樣結構體總共大小為8-byte。最后來驗證規則c,在VC中默認 的#pragma pack(n)中的n=8,而結構體中數據類型大小最大的為第四個成員int=4,故對齊模數為4,并且8 mode 4 = 0,所以滿足規則c。這樣整個結構體的總大小為8。
? ? ? ?對于dataAlign,第一個成員等于結構體變量首地址,偏移量為0,第二個成員為int=4,為了滿足規則b,需要在第一個成員之后填充3-byte,讓它相對于結構體首地址偏移量為4,結合運行結果,可知&dataAlign.a = 0x01109140,而&dataAlign.b = 0x01109144,它們之間相隔4-byte,0x01109141~0x01109143三個字節被0填 充。第三個成員short=2,無需填充滿足規則b。第四個成員char=1,也不需要填充。結構體總大小相加4 + 4 + 2 + 1 = 11。同樣最后需要驗證規則c,結構體中數據類型大小最大為第二個成員int=4,比VC默認對齊模數8小,故這個結構體的對齊模數仍然為4,顯然11 mode 4 != 0,故為了滿足規則c,需要在char后面填充一個字節,這樣結構體變量dataAlign的總大小為4 + 4 + 2 + 2 = 12。
總結
以上是生活随笔為你收集整理的[转]C++结构体|类 内存对齐详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: web页面 验证码 生成
- 下一篇: 转载 HTTPS 之fiddler抓包、