GCC的C語言擴展筆記(二)-- linux gcc的屬性解析

原本打算接着摘抄《GCC參考手冊》關於屬性擴展(__attribute__)的說明,不過因爲翻譯太差,許多地方看得莫名其妙,就到網上找了下,下面是一篇關於屬性的文章,講得不錯,轉帖於下。html

不敢掠美,附上原文地址:http://blog.eastmoney.com/stcu/blog_150699657.htmllinux

linux gcc的屬性解析 數組

GNU C的一大特點(卻不被初學者所知)就是__attribute__機制。__attribute__能夠設置函數屬性(Function Attribute)、變量屬性(Variable Attribute)和類型屬性(Type Attribute)。函數

__attribute__書寫特徵是:__attribute__先後都有兩個下劃線,並切後面會緊跟一對原括弧,括弧裏面是相應的__attribute__參數。測試

__attribute__語法格式爲:優化

__attribute__ ((attribute-list))ui

其位置約束爲:this

放於聲明的尾部「;」以前。spa

函數屬性(Function Attribute)

函數屬性能夠幫助開發者把一些特性添加到函數聲明中,從而可使編譯器在錯誤檢查方面的功能更強大。__attribute__機制也很容易同非GNU應用程序作到兼容之功效。.net

GNU CC須要使用 –Wall編譯器來擊活該功能,這是控制警告信息的一個很好的方式。下面介紹幾個常見的屬性參數。

__attribute__ format

該__attribute__屬性能夠給被聲明的函數加上相似printf或者scanf的特徵,它可使編譯器檢查函數聲明和函數實際調用參數之間的格式化字符串是否匹配。該功能十分有用,尤爲是處理一些很難發現的bug。

format的語法格式爲:

format (archetype, string-index, first-to-check)

format屬性告訴編譯器,按照printf, scanf, strftime或strfmon的參數表格式規則對該函數的參數進行檢查。「archetype」指定是哪一種風格;「string-index」指定傳入函數的第幾個參數是格式化字符串;「first-to-check」指定從函數的第幾個參數開始按上述規則進行檢查。

具體使用格式以下:

__attribute__((format(printf,m,n)))

__attribute__((format(scanf,m,n)))

其中參數m與n的含義爲:

m:第幾個參數爲格式化字符串(format string);

n:參數集合中的第一個,即參數「…」裏的第一個參數在函數參數總數排在第幾,注意,有時函數參數裏還有「隱身」的呢,後面會提到;

在使用上,__attribute__((format(printf,m,n)))是經常使用的,而另外一種卻不多見到。下面舉例說明,其中myprint爲本身定義的一個帶有可變參數的函數,其功能相似於printf:

//m=1;n=2
extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));

//m=2;n=3
extern void myprint(int l,const char *format,...) __attribute__((format(printf,2,3))); 

須要特別注意的是,若是myprint是一個函數的成員函數,那麼m和n的值可有點「懸乎」了,例如:

//m=3;n=4

extern void myprint(int l,const char *format,...) __attribute__((format(printf,3,4)));

其緣由是,類成員函數的第一個參數實際上一個「隱身」的「this」指針。(有點C++基礎的都知道點this指針,不知道你在這裏還知道嗎?)

這裏給出測試用例:attribute.c,代碼以下:

1:

2:extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));

3:

4:void test()

5:{

6:  myprint("i=%d\",6);

7:  myprint("i=%s\",6);

8:  myprint("i=%s\","abc");

9:  myprint("%s,%d,%d\",1,2);

10:} 

運行$gcc –Wall –c attribute.c attribute後,輸出結果爲:

attribute.c: In function `test':

attribute.c:7: warning: format argument is not a pointer (arg 2)

attribute.c:9: warning: format argument is not a pointer (arg 2)

attribute.c:9: warning: too few arguments for format

若是在attribute.c中的函數聲明去掉__attribute__((format(printf,1,2))),再從新編譯,既運行$gcc –Wall –c attribute.c attribute後,則並不會輸出任何警告信息。

注意,默認狀況下,編譯器是能識別相似printf的「標準」庫函數。

__attribute__ noreturn

該屬性通知編譯器函數從不返回值,當遇到相似函數須要返回值而卻不可能運行到返回值處就已經退出來的狀況,該屬性能夠避免出現錯誤信息。C庫函數中的abort()和exit()的聲明格式就採用了這種格式,以下所示:

extern void exit(int)   __attribute__((noreturn));

extern void abort(void) __attribute__((noreturn));

爲了方便理解,你們能夠參考以下的例子:

//name: noreturn.c  ;測試__attribute__((noreturn))

extern void myexit();

int test(int n)
{
     if ( n > 0 )
     {
         myexit();
         /* 程序不可能到達這裏*/
     }
     else
          return 0;
} 

編譯顯示的輸出信息爲:

$ gcc –Wall –c noreturn.c

noreturn.c: In function `test':

noreturn.c:12: warning: control reaches end of non-void function

警告信息也很好理解,由於你定義了一個有返回值的函數test卻有可能沒有返回值,程序固然不知道怎麼辦了!

加上__attribute__((noreturn))則能夠很好的處理相似這種問題。把
extern void myexit();
修改成:
extern void myexit() __attribute__((noreturn));
以後,編譯不會再出現警告信息。

__attribute__ const

該屬性只能用於帶有數值類型參數的函數上。當重複調用帶有數值參數的函數時,因爲返回值是相同的,因此此時編譯器能夠進行優化處理,除第一次須要運算外,其它只須要返回第一次的結果就能夠了,進而能夠提升效率。該屬性主要適用於沒有靜態狀態(static state)和反作用的一些函數,而且返回值僅僅依賴輸入的參數。

爲了說明問題,下面舉個很是「糟糕」的例子,該例子將重複調用一個帶有相同參數值的函數,具體以下:
extern int square(int n) __attribute__((const));
...
    for (i = 0; i < 100; i++ )
    {
         total += square(5) + i;
    }
經過添加__attribute__((const))聲明,編譯器只調用了函數一次,之後只是直接獲得了相同的一個返回值。

事實上,const參數不能用在帶有指針類型參數的函數中,由於該屬性不但影響函數的參數值,一樣也影響到了參數指向的數據,它可能會對代碼自己產生嚴重甚至是不可恢復的嚴重後果。

而且,帶有該屬性的函數不能有任何反作用或者是訪問全局或靜態變量,因此,相似getchar()或time()的函數是不適合使用該屬性的。

-finstrument-functions

該參數可使程序在編譯時,在函數的入口和出口處生成instrumentation調用。剛好在函數入口以後並剛好在函數出口以前,將使用當前函數的地址和調用地址來調用下面的 profiling 函數。(在一些平臺上,__builtin_return_address不能在超過當前函數範圍以外正常工做,因此調用地址信息可能對profiling函數是無效的。)

void __cyg_profile_func_enter(void *this_fn, void *call_site);
void __cyg_profile_func_exit(void *this_fn, void *call_site);

其中,第一個參數this_fn是當前函數的起始地址,可在符號表中找到;第二個參數call_site是指調用處地址。

instrumentation 也可用於在其它函數中展開的內聯函數。從概念上來講,profiling調用將指出在哪裏進入和退出內聯函數。這就意味着這種函數必須具備可尋址形式。若是函數包含內聯,而全部使用到該函數的程序都要把該內聯展開,這會額外地增長代碼長度。若是要在C 代碼中使用extern inline聲明,必須提供這種函數的可尋址形式。

可對函數指定no_instrument_function屬性,在這種狀況下不會進行instrumentation操做。例如,能夠在如下狀況下使用no_instrument_function屬性:上面列出的profiling函數、高優先級的中斷例程以及任何不能保證profiling正常調用的函數。

no_instrument_function

若是使用了-finstrument-functions ,將在絕大多數用戶編譯的函數的入口和出口點調用profiling函數。使用該屬性,將不進行instrument操做。

constructor/destructor

若函數被設定爲constructor屬性,則該函數會在main()函數執行以前被自動的執行。相似的,若函數被設定爲destructor屬性,則該函數會在main()函數執行以後或者exit()被調用後被自動的執行。擁有此類屬性的函數常常隱式的用在程序的初始化數據方面。

這兩個屬性尚未在面向對象C中實現。

同時使用多個屬性

能夠在同一個函數聲明裏使用多個__attribute__,而且實際應用中這種狀況是十分常見的。使用方式上,你能夠選擇兩個單獨的__attribute__,或者把它們寫在一塊兒,能夠參考下面的例子:

/* 把相似printf的消息傳遞給stderr 並退出 */
extern void die(const char *format, ...)
               __attribute__((noreturn))
               __attribute__((format(printf, 1, 2)));
 
或者寫成
 
extern void die(const char *format, ...)
               __attribute__((noreturn, format(printf, 1, 2)));
 
若是帶有該屬性的自定義函數追加到庫的頭文件裏,那麼因此調用該函數的程序都要作相應的檢查。

和非GNU編譯器的兼容性

慶幸的是,__attribute__設計的很是巧妙,很容易做到和其它編譯器保持兼容,也就是說,若是工做在其它的非GNU編譯器上,能夠很容易的忽略該屬性。即便__attribute__使用了多個參數,也能夠很容易的使用一對圓括弧進行處理,例如:

/* 若是使用的是非GNU C, 那麼就忽略__attribute__ */
#ifndef __GNUC__
#  define  __attribute__(x)  /*NOTHING*/
#endif
 
須要說明的是,__attribute__適用於函數的聲明而不是函數的定義。因此,當須要使用該屬性的函數時,必須在同一個文件裏進行聲明,例如:

/* 函數聲明 */
void die(const char *format, ...) __attribute__((noreturn))
                                  __attribute__((format(printf,1,2)));
 
void die(const char *format, ...)
{
               /* 函數定義 */
}
 
更多的屬性含義參考:
 

變量屬性(Variable Attributes)

關鍵字__attribute__也能夠對變量(variable)或結構體成員(structure field)進行屬性設置。這裏給出幾個經常使用的參數的解釋,更多的參數可參考本文給出的鏈接。

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

aligned (alignment)

該屬性規定變量或結構體成員的最小的對齊格式,以字節爲單位。例如:

int x __attribute__ ((aligned (16))) = 0;
 
編譯器將以16字節(注意是字節byte不是位bit)對齊的方式分配一個變量。也能夠對結構體成員變量設置該屬性,例如,建立一個雙字對齊的int對,能夠這麼寫:

struct foo { int x[2] __attribute__ ((aligned (8))); };
 
如上所述,你能夠手動指定對齊的格式,一樣,你也可使用默認的對齊方式。若是aligned後面不緊跟一個指定的數字值,那麼編譯器將依據你的目標機器狀況使用最大最有益的對齊方式。例如:

short array[3] __attribute__ ((aligned));
 
選擇針對目標機器最大的對齊方式,能夠提升拷貝操做的效率。

aligned屬性使被設置的對象佔用更多的空間,相反的,使用packed能夠減少對象佔用的空間。

須要注意的是,attribute屬性的效力與你的鏈接器也有關,若是你的鏈接器最大隻支持16字節對齊,那麼你此時定義32字節對齊也是無濟於事的。

packed

使用該屬性可使得變量或者結構體成員使用最小的對齊方式,即對變量是一字節對齊,對域(field)是位對齊。

下面的例子中,x成員變量使用了該屬性,則其值將緊放置在a的後面:
          struct test
          {
              char a;
              int x[2] __attribute__ ((packed));
          };
 

其它可選的屬性值還能夠是:cleanup,common,nocommon,deprecated,mode,section,shared,tls_model,transparent_union,unused,vector_size,weak,dllimport,dlexport等,詳細信息可參考:
http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes


類型屬性(Type Attribute)

關鍵字__attribute__也能夠對結構體(struct)或共用體(union)進行屬性設置。大體有六個參數值能夠被設定,即:aligned, packed, transparent_union, unused, deprecated 和 may_alias。

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

aligned (alignment)

該屬性設定一個指定大小的對齊格式(以字節爲單位),例如:
 
struct S { short f[3]; } __attribute__ ((aligned (8)));
typedef int more_aligned_int __attribute__ ((aligned (8)));

該聲明將強制編譯器確保(盡它所能)變量類型爲struct S或者more-aligned-int的變量在分配空間時採用8字節對齊方式。

如上所述,你能夠手動指定對齊的格式,一樣,你也可使用默認的對齊方式。若是aligned後面不緊跟一個指定的數字值,那麼編譯器將依據你的目標機器狀況使用最大最有益的對齊方式。例如:
 
struct S { short f[3]; } __attribute__ ((aligned));

這裏,若是sizeof(short)的大小爲2(byte),那麼,S的大小就爲6。取一個2的次方值,使得該值大於等於6,則該值爲8,因此編譯器將設置S類型的對齊方式爲8字節。

aligned屬性使被設置的對象佔用更多的空間,相反的,使用packed能夠減少對象佔用的空間。

須要注意的是,attribute屬性的效力與你的鏈接器也有關,若是你的鏈接器最大隻支持16字節對齊,那麼你此時定義32字節對齊也是無濟於事的。

packed

使用該屬性對struct或者union類型進行定義,設定其類型的每個變量的內存約束。當用在enum類型定義時,暗示了應該使用最小完整的類型(it indicates that the smallest integral type should be used)。

下面的例子中,my-packed-struct類型的變量數組中的值將會牢牢的靠在一塊兒,但內部的成員變量s不會被「pack」,若是但願內部的成員變量也被packed的話,my-unpacked-struct也須要使用packed進行相應的約束。
 
struct my_unpacked_struct
{
      char c;
      int i;
};

struct my_packed_struct
{
     char c;
     int  i;
     struct my_unpacked_struct s;
}__attribute__ ((__packed__));

其它屬性的含義見:

http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Type-Attributes.html#Type-Attributes


變量屬性與類型屬性舉例

下面的例子中使用__attribute__屬性定義了一些結構體及其變量,並給出了輸出結果和對結果的分析。

程序代碼爲:
struct p
{
    int a;
    char b;
    char c;
}__attribute__((aligned(4))) pp;

 
struct q
{
    int a;
    char b;
    struct p qn;
    char c;
}__attribute__((aligned(8))) qq;

int main()
{       
    printf("sizeof(int)=%d,sizeof(short)=%d.sizeof(char)=%d\n",
        sizeof(int),sizeof(short),sizeof(char));

    printf("pp=%d,qq=%d \n", sizeof(pp),sizeof(qq));

    return 0;
}
輸出結果:

sizeof(int)=4,sizeof(short)=2.sizeof(char)=1
pp=8,qq=24

分析:

sizeof(pp):
sizeof(a)+ sizeof(b)+ sizeof(c)=4+1+1=6<2 3 =8= sizeof(pp)
sizeof(qq):
sizeof(a)+ sizeof(b)=4+1=5

sizeof(qn)=8 ;即qn是採用8字節對齊的,因此要在a,b後面添3個空餘字節,而後才能存儲qn,

4+1+(3)+8+1=17

由於qq採用的對齊是8字節對齊,因此qq的大小一定是8的整數倍,即qq的大小是一個比17大又是8的倍數的一個最小值,由此獲得

17<24+8=24= sizeof(qq)

更詳細的介紹見:http://gcc.gnu.org

下面是一些便捷的鏈接:GCC 4.0 Function Attributes;GCC 4.0 Variable Attributes ;GCC 4.0 Type Attributes ;GCC 3.2 Function Attributes ;GCC 3.2 Variable Attributes ;GCC 3.2 Type Attributes ;GCC 3.1 Function Attributes ;GCC 3.1 Variable Attributes

Reference

1.有關__attribute__的相對簡單的介紹:http://www.unixwiz.net/techtips/gnu-c-attributes.html 2.__attribute__詳細介紹:http://gcc.gnu.org
相關文章
相關標籤/搜索