gcc之__attribute__簡介及對齊參數介紹

  GNU C的一大特點就是__attribute__機制。__attribute__機制能夠設置函數屬性(Function Attribute)、變量屬性(Variable Attribute)和類型屬性(Type Attribute)。html

  __attribute__語法格式爲:__attribute__((attribute-list))。函數

 

__attribute__對結構體(struct)或共用體(union)進行屬性設置:性能

  大體有六個參數值能夠被設定,即:aligned,packed,transparent_union,deprecated和may_aliasspa

  在使用__attribute__參數時,你也能夠在參數的先後都加上「__」(兩個下劃線),例如,使用__aligned__而不是aligned,這樣,你就能夠在相應的頭文件裏使用它而不須要關心頭文件裏是否有重名的宏定義。code

 

  1. aligned(alignment):htm

    設定對齊的格式(以字節爲單位),例如:對象

struct S {
  short b[3];  
}__attribute__((aligned (8)));
typedef int int32_t __attribute__((aligned(8)));
/*
* 該聲明將強制編譯器確保(儘量)變量類型爲structS或者int32_t的變量在分配空間時採用8字節對齊方式。
*/

  如上所述,你能夠手動指定對齊的格式,一樣,你也可使用默認的對齊方式。若是aligned後面不指定數值,那麼編譯器將依據你的目標機器狀況使用最大最有益的對齊方式。例如:blog

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對內存的讀取不是連續的,而是分塊讀取的,塊的大小隻能是一、二、四、八、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

相關文章
相關標籤/搜索