結構體位元組對齊和位域對齊 VC gcc

2021-06-29 03:42:50 字數 3438 閱讀 9276

分類: c/c++

2012-10-13 15:34 

474人閱讀收藏 

舉報**

(1)什麼是位元組對齊

乙個變數占用 n 個位元組,則該變數的起始位址必須能夠被 n 整除,即: 存放起始地 址 % n = 0,對於結構體而言, 這個 n 取其成員中的資料型別佔空間的值最大的那個。

(2)為什麼要位元組對齊

記憶體空間是按照位元組來劃分的,從理論上說對記憶體空間的訪問可以從任何位址開始,但是在實際上不同架構的 cpu 為了提高訪問記憶體的速度,就規定了對於某些型別的資料只能從特定的起始位置開始訪問。這樣就決定了各種資料型別只能按照相應的規則在記憶體空間中存放,而不能乙個接乙個的順序排列。

舉個例子,比如有些平台訪問記憶體位址都從偶數字址開始,對於乙個 int 型(假設 32 位系統),如果從偶數字址開始的地方存放,這樣乙個讀週期就可以讀出這個 int 資料,但是如果從奇數字址開始的位址存放,就需要兩個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到這個 int 資料,這樣明顯降低了讀取的效率。

(3)如何進行位元組對齊

每個成員按其型別的對齊引數(通常是這個型別的大小)和指定對齊引數(不指定則取預設值)中較小的乙個對齊,並且結構的長度必須為所用過的所有對齊引數的整數倍,不夠就補空位元組。

這個規則有點苦澀,可以把這個規則分解一下,前半句的意思先獲得對齊值後與指定對齊值進行比較,其中對齊值獲得方式如下:

1. 資料型別的自身對齊值為:對於 char 型資料,其自身對齊值為 1,對於 short 型 為 2,對於 int, long,float 型別,其自身對齊值為 4,對於 double 型別其自身對齊值為 8,單位為位元組。

2.結構體自身對齊值:其成員中自身對齊值最大的那個值。

其中指定對齊值獲得方式如下:

#pragma pack (value)時的指定對齊值 value。

未指定則取預設值。

後半句的意思是主要是針對於結構體的長度而言,因為針對資料型別的成員,它僅有乙個對齊引數,其本身的長度、於這個對齊引數,即 1 倍。對於結構體而言,它可能使用 了多種資料型別,那麼這句話翻譯成對齊規則:每個成員的起始位址 % 自身對齊值 = 0,如果不等於 0 則先補空位元組直至這個表示式成立。

換句話說,對於結構體而言,結構體在記憶體的存放順序用如下規則即可對映出來:

(一)每個成員的起始位址 % 每個成員的自身對齊值(注意這裡不是結構體的自身對齊值) = 0, 如果不等於 0 則先補空 位元組直至這個表示式成立;

(二)結構體的長度必須為結構體的自身對齊值的整數倍,不夠就補空位元組。

不同平台下對齊係數:

每個特定平台的編譯器都有乙個預設的對齊係數,gcc中是4,vc中是8。也可以通過於編譯命令#pragmapack(n)來指定該係數,經測試gcc中n的值只能是1,2和4。

舉個例子:

#pragmapack(8)

structa;

這樣做的目的主要是為了對程式設計師起乙個提示作用,如果不加則編譯器會自動補齊。

在gcc中

由於對齊係數最大只能為4,所以上述結構體占記憶體大小為:8,16,20,24,24。

驗證:1、預設情況(n=4)

struct st1 ;

整個結構體成員對齊後所佔的區間為[0,8],佔9個位元組,接著結構體本身對齊,成員中最長的是4,n等於1,所以結構體本身按1對齊(即對齊係數)。

整個結構體的大小 = 比整個結構體資料成員所佔的總空間大或相等且和對齊係數求模結果為0、與之距離最近的數。

本例中,9%1=0,所以結構體st1佔9個位元組的空間。

3、#pragmapack(2)(即n=2)

struct st1 ;

整個結構體成員對齊後所佔的區間為[0,8],佔9個位元組,接著結構體本身對齊,成員中最長的是4,n等於2,所以結構體本身按2對齊(即對齊係數)。

整個結構體的大小 = 比整個結構體資料成員所佔的總空間大或相等且和對齊係數求模結果為0、與之距離最近的數。

本例中,10%2=0,所以結構體st1佔10個位元組的空間。

為什麼說#pragmapack(n)中n只能是1,2,4呢?

比如3,如果n=3,在編譯的時候會警告「對齊邊界必須是 2 的較小次方,而不是 3」,也就是說是不起作用的,按預設對齊係數對齊。

再如8,會有什麼結果?看下一例:

4、#pragmapack(8)(即n=8)

struct siz ;

如果8起作用,分析一下:

struct siz ;

整個結構體成員對齊後所佔的區間為[0,19],佔20個位元組,接著結構體本身對齊,成員中最長的是8,n等於4,所以結構體本身按4對齊(即對齊係數)。20%4=0,所以佔20個位元組。與執行結果一致。

綜上分析,當n=8的時候gcc仍然使用的是預設的對齊係數4.

位域

有些資訊在儲存時,並不需要占用乙個完整的位元組, 而只需佔幾個或乙個二進位制位。例如在存放乙個開關量時,只有0和1兩種狀態, 用一位二進位即可。為了節省儲存空間,並使處理簡便,c語言又提供了一種資料結構,稱為「位域」或「位段」。所謂「位域」是把乙個位元組中的二進位劃分為幾個不同的區域,並說明每個區域的位數。每個域有乙個網域名稱,允許在程式中按網域名稱進行操作。 這樣就可以把幾個不同的物件用乙個位元組的二進位制位域來表示。一、位域的定義和位域變數的說明位域定義與結構定義相仿,其形式為:

struct 位域結構名 

;其中位域列表的形式為: 型別說明符 位網域名稱:位域長度 

例如: 

struct bs

; 如果結構體中含有位域(bit-field),那麼vc中準則又要有所更改:

1) 如果相鄰位域字段的型別相同,且其位寬之和小於型別的sizeof大小,則後面的字段將緊鄰前乙個字段儲存,直到不能容納為止;

2) 如果相鄰位域字段的型別相同,但其位寬之和大於型別的sizeof大小,則後面的字段將從新的儲存單元開始,其偏移量為其型別大小的整數倍;

3) 如果相鄰的位域字段的型別不同,則各編譯器的具體實現有差異,vc6採取不壓縮方式(不同位域字段存放在不同的位域型別位元組中),dev-c++和gcc都採取壓縮方式;

struct s1

;struct s2

;sizeof(struct s1), vc中為24,gcc中為 12

sizeof(struct s2), vc中為24,gcc中為 16

4) 如果位域字段之間穿插著非位域字段,則不進行壓縮;

備註:結構體

typedef struct

n3;在gcc下佔據的空間為16位元組,在vc下佔據的空間是24個位元組。

5) 整個結構體的總大小為最寬基本型別成員大小的整數倍。

ps:

結構體位元組對齊與位域

code advance struct c includestruct person int main 1.本身的成員變數型別 2.結構體存在位元組對齊 結構體內部最大的單成員型別的整數倍 如果下乙個成員無法在有限的空間存放則需要乙個額外的空間存放 3.如何優化位元組對齊 1.按照有限空間合理排布成...

位域位元組對齊

使用位域的主要目的是壓縮儲存,其大致規則為 1 如果相鄰位域字段的型別相同,且其位寬之和小於型別的sizeof大小,則後面的字段將緊鄰前乙個字段儲存,直到不能容納為止 2 如果相鄰位域字段的型別相同,但其位寬之和大於型別的sizeof大小,則後面的字段將從新的儲存單元開始,其偏移量為其型別大小的整數...

C語言結構體位元組對齊與位域

一般的結構體 位域 include struct stu1 12位元組 可以看到,結構體大小並不是簡單的位元組相加。那麼,結構體為什麼不設定成位元組簡單相加而去為難我們呢?解 為了提高定址速度 我們可以試想結構體在呼叫成員的情況。比如呼叫成員c。它先找到我們定義的一大塊記憶體結構體s的首位址,flo...