標籤:style blog color io 資料 for 問題 ar
引言
首先看一個C語言下結構體的小程式。
#include<stdio.h>struct StudentInfo { char i; int j;};void main() { printf("%d\n",sizeof(struct StudentInfo)); }
輸出結果:8
不解,以為是5。其實這涉及到電腦記憶體對齊的問題,在電腦群組成原理中有介紹。
概述
許多實際的電腦系統對基本類型資料在記憶體中存放的位置有限制,它們會要求這些資料的首地址的值是某個數k(通常它為4或8)的倍數,這就是所謂的記憶體對齊,而這個k則被稱為該資料類型的對齊模數(alignment modulus)。當一種類型S的對齊模數與另一種類型T的對齊模數的比值是大於1的整數,我們就稱類型S的對齊要求比T強(嚴格),而稱T比S弱(寬鬆)。這種強制的要求一來簡化了處理器與記憶體之間傳輸系統的設計,二來可以提升讀取資料的速度。比如這麼一種處理器,它每次讀寫記憶體的時候都從某個8倍數的地址開始,一次讀出或寫入8個位元組的資料,假如軟體能保證double類型的資料都從8倍數地址開始,那麼讀或寫一個double類型資料就只需要一次記憶體操作。否則,我們就可能需要兩次記憶體操作才能完成這個動作,因為資料或許恰好橫跨在兩個符合對齊要求的8位元組記憶體塊上。某些處理器在資料不滿足對齊要求的情況下可能會出錯,但是Intel的IA32架構的處理器則不管資料是否對齊都能正確工作。不過Intel奉勸大家,如果想提升效能,那麼所有的程式資料都應該儘可能地對齊。
VC編輯器下的記憶體對齊
ANSI C標準中並沒有規定,相鄰聲明的變數在記憶體中一定要相鄰。為了程式的高效性,記憶體對齊問題由編譯器自行靈活處理,這樣導致相鄰的變數之間可能會有一些填充位元組。對於基礎資料型別 (Elementary Data Type)(int char),他們佔用的記憶體空間在一個確定硬體系統下有個確定的值,所以,接下來我們只是考慮結構體成員記憶體配置情況。
Win32平台下的微軟C編譯器(cl.exe for 80×86)的對齊策略:
1) 結構體變數的首地址能夠被其最寬基本類型成員的大小所整除;
備忘:編譯器在給結構體開闢空間時,首先找到結構體中最寬的基礎資料型別 (Elementary Data Type),然後尋找記憶體位址能被該基礎資料型別 (Elementary Data Type)所整除的位置,作為結構體的首地址。將這個最寬的基礎資料型別 (Elementary Data Type)的大小作為上面介紹的對齊模數。
2) 結構體每個成員相對於結構體首地址的位移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組(internal adding);
備忘:為結構體的一個成員開闢空間之前,編譯器首先檢查預開闢空間的首地址相對於結構體首地址的位移是否是本成員的整數倍,若是,則存放本成員,反之,則在本成員和上一個成員之間填充一定的位元組,以達到整數倍的要求,也就是將預開闢空間的首地址後移幾個位元組。
3) 結構體的總大小為結構體最寬基本類型成員大小的整數倍,如有需要,編譯器會在最末一個成員之後加上填充位元組(trailing padding)。
備忘:結構體總大小是包括填充位元組,最後一個成員滿足上面兩條以外,還必須滿足第三條,否則就必須在最後填充幾個位元組以達到本條要求。
結構體的記憶體對齊規則
1)結構體成員按低地址到高地址的順序儲存在記憶體, 即按聲明的順序儲存
2)每個成員的地址必須滿足: 是 sizeof(該成員) 的整數倍
3)總的位元組數是 最大內建(就是基本類型)成員所佔的位元組數的 整數倍