GNU C的一大特色就是__attribute__機制。__attribute__機制可以設置函數屬性(Function Attribute)、變量屬性(Variable Attribute)和類型屬性(Type Attribute)。
__attribute__語法格式為:__attribute__((attribute-list))。
__attribute__對結構體(struct)或共用體(union)進行屬性設置:
大致有六個參數值可以被設定,即:aligned,packed,transparent_union,deprecated和may_alias。
在使用__attribute__參數時,你也可以在參數的前后都加上“__”(兩個下划線),例如,使用__aligned__而不是aligned,這樣,你就可以在相應的頭文件里使用它而不需要關心頭文件里是否有重名的宏定義。
1. aligned(alignment):
設定對齊的格式(以字節為單位),例如:
struct S { short b[3]; }__attribute__((aligned (8)));
typedef int int32_t __attribute__((aligned(8)));
/*
* 該聲明將強制編譯器確保(盡可能)變量類型為structS或者int32_t的變量在分配空間時采用8字節對齊方式。
*/
如上所述,你可以手動指定對齊的格式,同樣,你也可以使用默認的對齊方式。如果aligned后面不指定數值,那么編譯器將依據你的目標機器情況使用最大最有益的對齊方式。例如:
struct S { short b[3]; } __attribute__((aligned)); // 使用默認對齊方式,依據目標機器,使用最大最有益的對齊方式
#include <stdio.h> struct S1 { short b[3]; }; struct S2 { short b[3]; } __attribute__((aligned(8))); struct S3 { short b[3]; } __attribute__((aligned(16))); struct S4 { short b[3]; } __attribute__((aligned(32))); struct S5 { short b[3]; } __attribute__((aligned(64))); struct S6 { short b[3]; } __attribute__((aligned)); int main(int argc, char** argv) { printf("sizeof(struct S1) = %ld\n", sizeof(struct S1)); printf("sizeof(struct S2) = %ld\n", sizeof(struct S2)); printf("sizeof(struct S3) = %ld\n", sizeof(struct S3)); printf("sizeof(struct S4) = %ld\n", sizeof(struct S4)); printf("sizeof(struct S5) = %ld\n", sizeof(struct S5)); printf("sizeof(struct S6) = %ld\n", sizeof(struct S6)); return 0; }
/*
* 輸出結果:
* sizeof(struct S1) = 6
* sizeof(struct S2) = 8
* sizeof(struct S3) = 16
* sizeof(struct S4) = 32
* sizeof(struct S5) = 64
* sizeof(struct S6) = 16
*/
注意:__attribute__屬性的效力與你的鏈接器有關,如果你的鏈接器最大只支持16字節對齊,那么你此時定義32字節對齊也無濟於事。
#include <stdio.h> struct A { int a; char b; short c; } aa; struct AP { int a; char b; short c; } __attribute__((aligned(16))) ap; struct B { char a; int b; short c; } bb; struct BP { char a; int b; short c; } __attribute__((aligned(4))) bp; struct C { int a; char b; struct AP px; short c; } cc; struct CP1 { int a; char b; struct AP px; short c; } __attribute__((aligned(4))) cp1; struct CP2 { int a; char b; struct AP px; short c; } __attribute__((aligned(8))) cp2; int main(int argc, char** argv) { printf("sizeof(aa) = %lu, sizeof(ap) = %lu\n", sizeof(aa), sizeof(ap)); printf("sizeof(bb) = %lu, sizeof(bp) = %lu\n", sizeof(bb), sizeof(bp)); printf("sizeof(cc) = %lu, sizeof(cp1) = %lu\n", sizeof(cc), sizeof(cp1)); printf("sizeof(cc) = %lu, sizeof(cp2) = %lu\n", sizeof(cc), sizeof(cp2)); return 0; }
/*
* 輸出結果:
* sizeof(aa) = 8, sizeof(ap) = 16
* sizeof(bb) = 12, sizeof(bp) = 12
* sizeof(cc) = 48, sizeof(cp1) = 48
* sizeof(cc) = 48, sizeof(cp2) = 48
*/
關於內存對齊:(其中的#pragma pack()在gcc中很少見,具體用法需確認)
① 什么是內存對齊?
不同類型的數據在內存中按照一定的規則排列;而不是順序的一個接一個的排放,這就是對齊。
#include <stdio.h> struct Test1 { char c1; short s; char c2; int i; }; struct Test2 { char c1; char c2; short s; int i; }; int main(int argc, char** argv) { printf("sizeof(struct Test1) = %lu\n", sizeof(struct Test1)); printf("sizeof(struct Test2) = %lu\n", sizeof(struct Test2)); return 0; } /* 輸出結果 * sizeof(struct Test1) = 12 * sizeof(struct Test2) = 8 */
②為什么需要內存對齊?
-
- CPU對內存的讀取不是連續的,而是分塊讀取的,塊的大小只能是1、2、4、8、16字節
- 當讀取操作的數據未對齊,則需要兩次總線周期來訪問內存,因此性能會大打折扣
- 某些硬件平台只能從規定的地址處取某些特定類型的數據,否則則拋出硬件異常
#pragma pack能夠改變編譯器的默認對齊方式
#include <stdio.h> #pragma pack(2) struct Test1 { char c1; short s; char c2; int i; }; #pragma pack() #pragma pack(4) struct Test2 { char c1; char c2; short s; int i; }; #pragma pack() int main(int argc, char** argv) { printf("sizeof(struct Test1) = %lu\n", sizeof(struct Test1)); printf("sizeof(struct Test2) = %lu\n", sizeof(struct Test2)); return 0; } /* * 輸出結果: * sizeof(struct Test1) = 10 * sizeof(struct Test2) = 8 */
- struct 占用的內存大小
- 第一個成員起始於0偏移處
- 每個成員按其類型大小和指定對齊參數n中較小的一個進行對齊
- 偏移地址和成員占用大小均需對齊
- 結構體成員的對齊參數為其所有成員使用的對其參數的最大值
- 結構體的總長度必須為所有對其參數的整數倍
#include <stdio.h> #pragma pack(8) struct S1 { short a; long b; }; struct S2 { char c; struct S1 d; double e; }; #pragma pack() int main() { struct S2 s2; printf("sizeof(struct S1) = %lu\n", sizeof(struct S1)); printf("sizeof(struct S2) = %lu\n", sizeof(struct S2)); printf("%d\n", sizeof(long)); printf("%d\n", (int)&(s2.d) - (int)&(s2.c)); return 0; } /* * 輸出結果: * sizeof(struct S1) = 16 * sizeof(struct S2) = 32 * 8 * 8 */
aligned屬性被設置的對象占用更多的空間,相反的,使用packed可以減小對象占用的空間。
2. packed:
使用該屬性對struct或union類型進行定義,設定其類型的每一個變量的內存約束。當用在enum類型定義時,暗示了應該使用最小完整的類型(it indicates that the smallest integral type should be used)。
#include <stdio.h> struct unpacked_struct { char c; int i; }; struct packed_struct_1 { char c; int i; } __attribute__((__packed__)); struct packed_struct_2 { char c; int i; struct unpacked_struct us; } __attribute__((__packed__)); int main(int argc, char** argv) { printf("sizeof(struct unpacked_struct) = %lu\n", sizeof(struct unpacked_struct)); printf("sizeof(struct packed_struct_1) = %lu\n", sizeof(struct packed_struct_1)); printf("sizeof(struct packed_struct_2) = %lu\n", sizeof(struct packed_struct_2)); return 0; }
/*
* 輸出:
* sizeof(struct unpacked_struct) = 8
* sizeof(struct packed_struct_1) = 5
* sizeof(struct packed_struct_2) = 13
*/
參考文章:
http://www.cnblogs.com/astwish/p/3460618.html