一、概念
對齊跟資料在記憶體中的位置有關,如果一個變數的記憶體地址正好位于它長度的整數倍,他就被稱做自然對齊,比如在32位cpu下,假設一個整型變數的地址為0x00000004,那它就是自然對齊的,
二、為什么要位元組對齊
需要位元組對齊的根本原因在于CPU訪問資料的效率問題,假設上面整型變數的地址不是自然對齊,比如為0x00000002,則CPU如果取它的值的話需要訪問兩次記憶體,第一次取從0x00000002-0x00000003的一個short,第二次取從0x00000004-0x00000005的一個short然后組合得到所要的資料,如果變數在0x00000003地址上的話則要訪問三次記憶體,第一次為char,第二次為short,第三次為char,然后組合得到整型資料,而如果變數在自然對齊位置上,則只要一次就可以取出資料,一些系統對對齊要求非常嚴格,比如sparc系統,如果取未對齊的資料會發生錯誤,舉個例:
char ch[8];
char *p = &ch[1];
int i = *(int *)p;
運行時會報segment error,而在x86上就不會出現錯誤,只是效率下降,
三、正確處理位元組對齊
對于標準資料型別,它的地址只要是它的長度的整數倍就行了,而非標準資料型別按下面的原則對齊:
陣列 :按斬訓本資料型別對齊,第一個對齊了后面的自然也就對齊了,
聯合 :按其包含的長度最大的資料型別對齊,
結構體: 結構體中每個資料型別都要對齊,
比如有如下一個結構體:
struct stu{
char sex;
int length;
char name[10];
};
struct stu my_stu;
由于在x86下,GCC默認按4位元組對齊,它會在sex后面跟name后面分別填充三個和兩個位元組使length和整個結構體對齊,于是我們sizeof(my_stu)會得到長度為20,而不是15.
四、__attribute__選項
我們可以按照自己設定的對齊大小來編譯程式,GNU使用__attribute__選項來設定,比如我們想讓剛才的結構按一位元組對齊,我們可以這樣定義結構體
struct stu{
char sex;
int length;
char name[10];
}attribute ((aligned (1)));
struct stu my_stu;
則sizeof(my_stu)可以得到大小為15,
上面的定義等同于
struct stu{
char sex;
int length;
char name[10];
}attribute ((packed));
struct stu my_stu;
__attribute__((packed))得變數或者結構體成員使用最小的對齊方式,即對變數是一位元組對齊,對域(field)是位對齊.
五、什么時候需要設定對齊
在設計不同CPU下的通信協議時,或者撰寫硬體驅動程式時暫存器的結構這兩個地方都需要按一位元組對齊,即使看起來本來就自然對齊的也要使其對齊,以免不同的編譯器生成的代碼不一樣.
一、快速理解
1. 什么是位元組對齊?
在C語言中,結構是一種復合資料型別,其構成元素既可以是基本資料型別(如int、long、float等)的變數,也可以是一些復合資料型別(如陣列、結構、聯合等)的資料單元,在結構中,編譯器為結構的每個成員按其自然邊界(alignment)分配空間,各個成員按照它們被宣告的順序在記憶體中順序存盤,第一個成員的地址和整個結構的地址相同,
為了使CPU能夠對變數進行快速的訪問,變數的起始地址應該具有某些特性,即所謂的”對齊”. 比如4位元組的int型,其起始地址應該位于4位元組的邊界上,即起始地址能夠被4整除.
2. 位元組對齊有什么作用?
位元組對齊的作用不僅是便于cpu快速訪問,同時合理的利用位元組對齊可以有效地節省存盤空間,
對于32位機來說,4位元組對齊能夠使cpu訪問速度提高,比如說一個long型別的變數,如果跨越了4位元組邊界存盤,那么cpu要讀取兩次,這樣效率就低了,但是在32位機中使用1位元組或者2位元組對齊,反而會使變數訪問速度降低,所以這要考慮處理器型別,另外還得考慮編譯器的型別,在vc中默認是4位元組對齊的,GNU gcc 也是默認4位元組對齊,
3. 更改C編譯器的預設位元組對齊方式
在預設情況下,C編譯器為每一個變數或是資料單元按其自然對界條件分配空間,一般地,可以通過下面的方法來改變預設的對界條件:
· 使用偽指令#pragma pack (n),C編譯器將按照n個位元組對齊,
· 使用偽指令#pragma pack (),取消自定義位元組對齊方式,
另外,還有如下的一種方式:
· __attribute((aligned (n))),讓所作用的結構成員對齊在n位元組自然邊界上,如果結構中有成員的長度大于n,則按照最大成員的長度來對齊,
· attribute ((packed)),取消結構在編譯程序中的優化對齊,按照實際占用位元組數進行對齊,
二、深入理解
什么是位元組對齊,為什么要對齊?
TragicJun 發表于 2006-9-18 9:41:00 現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何地址開始,但實際情況是在訪問特定型別變數的時候經常在特定的記憶體地址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊,
對齊的作用和原因:各個硬體平臺對存盤空間的處理上有很大的不同,一些平臺對某些特定型別的資料只能從某些特定地址開始存取,比如有些架構的CPU在訪問一個沒有進行對齊的變數的時候會發生錯誤,那么在這種架構下編程必須保證位元組對齊.其他平臺可能沒有這種情況,但是最常見的是如果不按照適合其平臺要求對資料存放進行對齊,會在存取效率上帶來損失,比如有些平臺每次讀都是從偶地址開始,如果一個int型(假設為32位系統)如果存放在偶地址開始的地方,那么一個讀周期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀周期,并對兩次讀出的結果的高低位元組進行拼湊才能得到該32bit資料,顯然在讀取效率上下降很多,
三.編譯器是按照什么樣的原則進行對齊的?
先讓我們看四個重要的基本概念:
1.資料型別自身的對齊值:
對于char型資料,其自身對齊值為1,對于short型為2,對于int,float,double型別,其自身對齊值為4,單位位元組,
2.結構體或者類的自身對齊值:其成員中自身對齊值最大的那個值,
3.指定對齊值:#pragma pack (value)時的指定對齊值value,
4.資料成員、結構體和類的有效對齊值:自身對齊值和指定對齊值中小的那個值,
有了這些值,我們就可以很方便的來討論具體資料結構的成員和其自身的對齊方式,有效對齊值N是最終用來決定資料存放地址方式的值,最重要,
有效對齊N,就是表示“對齊在N上”,也就是說該資料的"存放起始地址%N=0".而資料結構中的資料變數都是按定義的先后順序來排放的,
第一個資料變數的起始地址就是資料結構的起始地址,
結構體的成員變數要對齊排放,結構體本身也要根據自身的有效對齊值圓整(就是結構體成員變數占用總長度需要是對結構體有效對齊值的整數倍,)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/298225.html
標籤:C
