gcc數據對齊之: howto 1.


GCC支持用__attribute__為變量、類型、函數、標簽指定特殊屬性。這些不是編程語言標准里的內容,而屬於編譯器對語言的擴展。 本文介紹其中的兩個屬性:aligned和packed。

aligned

aligned屬性最常用在變量聲明上。它的作用是告訴GCC,為變量分配內存時,要分配在對齊的內存地址上。什么是對齊的內存地址呢?

一般計算機的內存是以字節(byte,等於8bit)為最小單元的。內存地址相當於從0開始的字節偏移數。如果一個內存地址是N的倍數,我們就說它是N字節對齊的(N-byte aligned)。

對於C/C++中的基本數據類型,假設它的長度為n字節,那么該類型的變量會被編譯器默認分配到n字節對齊的內存上。例如,char的長度是1字節,char類型變量的地址將是1字節對齊的(任意值均可); int的長度是4字節,所以int類型變量將被分配到4字節對齊的地址上。這種默認情況下的變量對齊方式又稱作自然對齊(naturally aligned)。

但是,有時候我們希望改變這種默認情況。這時候就可以使用aligned屬性了。例如:

int x __attribute__ ((aligned (16))) = 0;

告訴編譯器把變量x分配在16字節對齊的內存地址上,而非默認的4字節對齊。

編譯器之所以默認讓變量自然對齊,是因為這種對齊情況下的內存訪問是最高效的。受限於硬件實現,非對齊內存訪問的性能會有所下降;甚至在有些處理器(如DSP、早期的ARM)上,非對齊的內存訪問根本就不支持, 將直接引發錯誤。而我們之所以需要自己指定對齊方式,很多時候是程序優化的需求。例如,在x86平台上要使用SSE指令,所操作的數據在內存中就必須是16字節對齊的。

aligned不僅可以用作變量屬性,還能用作函數屬性和數據類型屬性。它作為函數屬性時的作用等價於對函數使用-falign-functions這一優化選項。 當它用作數據類型的屬性時,相當於告訴編譯器,這一類型的所有變量都要按指定字節數對齊。

aligned與結構體

結構體是一種數據類型,它與aligned有一些特殊的關系。上面提到,aligned可以用於指定數據類型的屬性,因此可以用於結構體,例如:

struct __attribute__ ((aligned (8))) my_struct1 {
	short f[3];
};

但這么用不僅會影響為該結構體變量分配內存的位置,還可能影響其內存占用。在上面的例子中,這個結構體內有3個兩字節的short,本來只需要占用6字節的內存; 但由於指定了8字節對齊,編譯器會在該結構體尾部填充額外的2個字節,使得這個結構體的大小為8字節。為什么要填充多余的字節呢?因為只有將該結構體補足8個字節,才能保證在這個結構體類型的數組中,數組中每個元素 都是8字節對齊的(考慮到數組元素在內存中的連續存放)。

aligned也可以用在結構體的成員上,這時就成了對變量指定屬性。GCC的相關文檔里提到,C語言規定結構體類型必須至少對齊到其所有成員變量對齊字節數的最小公倍數, 因此指定結構體的對齊完全可以通過指定其中成員變量的對齊來實現,不過前者明顯可讀性好些。

當aligned屬性用於數據類型(比如結構體)的時候,只能增加對齊字節數而不能減小。例如,下面的結構體本身大小有6字節,雖然指定了4字節對齊,但並不能達到目的,最終GCC還是會按8字節對齊處理。

struct __attribute__ ((aligned (4))) my_struct2 {
	short f[3];
};

如果要減小對齊字節數,需要用到下面介紹的packed屬性。

packed

packed屬性的主要目的是讓編譯器更緊湊地使用內存。當它用於變量時,告訴編譯器該變量應該有盡可能小的對齊,也就是1字節對齊。當它用於結構體時 ,相當於給該結構體的每個成員加上了packed屬性,這時該結構體將占用盡可能少的內存。例如:

struct __attribute__ ((packed)) my_struct3 {
	char c;
	int  i;
};

這個結構體被指定了packed,所以它的成員變量將是1字節對齊的,也就是說成員i將緊跟着成員c,從而使得該結構體的實際大小為5字節。 如果不指定packed,由於要滿足成員i的4字節對齊要求(它是int型的),編譯器將在成員c之后填充3個字節,使得這個結構體實際大小變為8字節。

采用packed屬性雖然可以節省內存,但它會導致非對齊的內存訪問。例如上述結構體的int型成員變量i,它的內存地址將不是4的倍數,訪問它時就是非對齊訪問。 當用.或->操作符存取結構體成員時,編譯器會保證存取到正確的值;但如果用指針直接訪問非對齊的成員變量,就只能指望處理器支持非對齊訪問了,否則將會出錯。 這也是很多人認為給結構體指定packed屬性不太安全的原因。

什么時候用packed?

上面說到使用packed可能“不安全”,但為什么還要用呢?什么時候會需要它呢?

使用packed最重要的場合莫過於處理跟文件格式或網絡協議有關的二進制數據了。這些格式或協議是不能容忍多余字節的,所以當用結構體表示其數據時,必須阻止編譯器填充字節。 這正是packed的設計初衷。

另外還可以用packed來節省內存,不過這是以犧牲性能為代價的,不是什么好方法。通過適當排列結構體成員的順序可以使得需要填充的字節數盡可能少,這才是應該考慮的措施。

我在最近的一個項目中需要從RGB像素生成Bitmap(位圖)文件。Bitmap文件的頭信息是用結構體描述的。 一開始寫入磁盤的文件總是不對,分析發現正是因為編譯器在我的結構體里填充了多余字節來滿足對齊的要求。簡單一個packed就解決了這個問題。

但正如前面所說,使用packed屬性具有潛在的問題。如果一個結構體被packed了,盡量不要使用指向其內部成員變量的指針,除非你真的知道你在做什么。

 

轉載:

http://blog.shengbin.me/posts/gcc-attribute-aligned-and-packed


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM