__attribute__机制介绍
1. __attribute__
GNU C的一大特色(卻不被初學者所知)就是__attribute__機制。
__attribute__可以設置函數(shù)屬性(Function Attribute)、變量屬性(Variable Attribute)和類型屬性(Type Attribute)
__attribute__前后都有兩個下劃線,并且后面會緊跟一對原括弧,括弧里面是相應的__attribute__參數(shù)
__attribute__語法格式為:
__attribute__ ( ( attribute-list ) )
函數(shù)屬性(Function Attribute),函數(shù)屬性可以幫助開發(fā)者把一些特性添加到函數(shù)聲明中,從而可以使編譯器在錯誤檢查方面的功能更強大。
__attribute__機制也很容易同非GNU應用程序做到兼容。
GNU CC需要使用 –Wall,這是控制警告信息的一個很好的方式。下面介紹幾個常見的屬性參數(shù)。
?
2. format
該屬性可以使編譯器檢查函數(shù)聲明和函數(shù)實際調(diào)用參數(shù)之間的格式化字符串是否匹配。它可以給被聲明的函數(shù)加上類似printf或者scanf的特征,該功能十分有用,尤其是處理一些很難發(fā)現(xiàn)的bug。
format的語法格式為:
format ( archetype,? string-index,? first-to-check )
format屬性告訴編譯器,按照printf,scanf,strftime或strfmon的參數(shù)表格式規(guī)則對該函數(shù)的參數(shù)進行檢查。archetype:指定是哪種風格;
string-index:指定傳入函數(shù)的第幾個參數(shù)是格式化字符串;
first-to-check:指定從函數(shù)的第幾個參數(shù)開始按上述規(guī)則進行檢查。
具體使用格式如下:
__attribute__( ( format( printf,m,n ) ) )
__attribute__( ( format( scanf,m,n ) ) )
其中參數(shù)m與n的含義為:
m:第幾個參數(shù)為格式化字符串(format string);
n:參數(shù)集合中的第一個,即參數(shù)“…”里的第一個參數(shù)在函數(shù)參數(shù)總數(shù)排在第幾
注意,有時函數(shù)參數(shù)里還有“隱身”的呢,后面會提到;
在使用上,__attribute__((format(printf,m,n)))是常用的,而另一種卻很少見到。
下面舉例說明,其中myprint為自己定義的一個帶有可變參數(shù)的函數(shù),其功能類似于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是一個函數(shù)的成員函數(shù),那么m和n的值可有點“懸乎”了,例如:
//m=3;n=4
extern void? myprint( int l,const char *format,... ) __attribute__( ( format( printf,3,4 ) ) );
其原因是,類成員函數(shù)的第一個參數(shù)實際上一個“隱身”的“this”指針。(有點C++基礎的都知道點this指針,不知道你在這里還知道嗎?)
這里給出測試用例:attribute.c,代碼如下:
extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));
void test()
{
?? ? ? ? myprint("i=%d/n",6);
?? ? ? ? myprint("i=%s/n",6);
?? ? ? ? myprint("i=%s/n","abc");
? ? ? ? myprint("%s,%d,%d/n",1,2);
}
運行$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中的函數(shù)聲明去掉__attribute__((format(printf,1,2))),再重新編譯,
既運行$gcc –Wall –c attribute.c attribute后,則并不會輸出任何警告信息。
注意,默認情況下,編譯器是能識別類似printf的“標準”庫函數(shù)。
?
3. noreturn
該屬性通知編譯器函數(shù)從不返回值。
當遇到函數(shù)需要返回值卻還沒運行到返回值處就已退出來的情況,該屬性可以避免出現(xiàn)錯誤信息。C庫函數(shù)中的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
警告信息也很好理解,因為你定義了一個有返回值的函數(shù)test卻有可能沒有返回值,程序當然不知道怎么辦了!加上__attribute__((noreturn))則可以很好的處理類似這種問題。把extern void myexit();修改為:
extern void? myexit() __attribute__((noreturn));
之后,編譯不會再出現(xiàn)警告信息。
?
4. const
該屬性只能用于帶有數(shù)值類型參數(shù)的函數(shù)上,當重復調(diào)用帶有數(shù)值參數(shù)的函數(shù)時,由于返回值是相同的。所以此時編譯器可以進行優(yōu)化處理,除第一次需要運算外, 其它只需要返回第一次的結果。
該屬性主要適用于沒有靜態(tài)狀態(tài)(static state)和副作用的一些函數(shù),并且返回值僅僅依賴輸入的參數(shù)。為了說明問題,下面舉個非常“糟糕”的例子,該例子將重復調(diào)用一個帶有相同參數(shù)值的函數(shù),具體如下:
extern int? square( int? n ) __attribute__ ( (const) );
for (i = 0; i < 100; i++ )? ? ? ? ? ? ? ? ?
{ ? ? ?
?? ? ? total += square (5) + i; ? ? ? ? ? ?
}
添加__attribute__((const))聲明,編譯器只調(diào)用了函數(shù)一次,以后只是直接得到了相同的一個返回值。
事實上,const參數(shù)不能用在帶有指針類型參數(shù)的函數(shù)中,因為該屬性不但影響函數(shù)的參數(shù)值,同樣也影響到了參數(shù)指向的數(shù)據(jù),它可能會對代碼本身產(chǎn)生嚴重甚至是不可恢復的嚴重后果。并且,帶有該屬性的函數(shù)不能有任何副作用或者是靜態(tài)的狀態(tài),類似getchar()或time()的函數(shù)是不適合使用該屬性。
?
5. finstrument-functions
該參數(shù)可以使程序在編譯時,在函數(shù)的入口和出口處生成instrumentation調(diào)用。恰好在函數(shù)入口之后并恰好在函數(shù)出口之前,將使用當前函數(shù)的地址和調(diào)用地址來調(diào)用下面的profiling函數(shù)。(在一些平臺上,__builtin_return_address不能在超過當前函數(shù)范圍之外正常工作,所以調(diào)用地址信息可能對profiling函數(shù)是無效的)
void? __cyg_profile_func_enter( void? *this_fn,void? *call_site );
void? __cyg_profile_func_exit( void? *this_fn,void? *call_site );
其中,第一個參數(shù)this_fn是當前函數(shù)的起始地址,可在符號表中找到;第二個參數(shù)call_site是調(diào)用處地址。
?
6. instrumentation
也可用于在其它函數(shù)中展開的內(nèi)聯(lián)函數(shù)。從概念上來說,profiling調(diào)用將指出在哪里進入和退出內(nèi)聯(lián)函數(shù)。這就意味著這種函數(shù)必須具有可尋址形式。如果函數(shù)包含內(nèi)聯(lián),而所有使用到該函數(shù)的程序都要把該內(nèi)聯(lián)展開,這會額外地增加代碼長度。如果要在C 代碼中使用extern inline聲明,必須提供這種函數(shù)的可尋址形式。
可對函數(shù)指定no_instrument_function屬性,在這種情況下不會進行 instrumentation操作。例如,可以在以下情況下使用no_instrument_function屬性:上面列出的profiling函數(shù)、高優(yōu)先級的中斷例程以及任何不能保證profiling正常調(diào)用的函數(shù)。
no_instrument_function
如果使用了-finstrument-functions,將在絕大多數(shù)用戶編譯的函數(shù)的入口和出口點調(diào)用profiling函數(shù)。使用該屬性,將不進行instrument操作。
?
7. constructor/destructor
若函數(shù)被設定為constructor屬性,則該函數(shù)會在main()函數(shù)執(zhí)行之前被自動的執(zhí)行。類似的,若函數(shù)被設定為destructor屬性,則該函數(shù)會在main()函數(shù)執(zhí)行之后或者exit()被調(diào)用后被自動的執(zhí)行。擁有此類屬性的函數(shù)經(jīng)常隱式的用在程序的初始化數(shù)據(jù)方面,這兩個屬性還沒有在面向?qū)ο?span style="line-height:normal; font-family:Helvetica">C中實現(xiàn)。
?
8. 同時使用多個屬性
可以在同一個函數(shù)聲明里使用多個__attribute__,并且實際應用中這種情況是十分常見的。使用方式上,你可以選擇兩個單獨的__attribute__,或者把它們寫在一起,可以參考下面的例子:
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)) );
如果帶有該屬性的自定義函數(shù)追加到庫的頭文件里,那么所以調(diào)用該函數(shù)的程序都要做相應的檢查。
?
9. 和非GNU編譯器的兼容性
__attribute__設計的非常巧妙,很容易作到和其它編譯器保持兼容。也就是說,如果工作在其它的非GNU編譯器上,可以很容易的忽略該屬性。即使__attribute__使用了多個參數(shù),也可以很容易的使用一對圓括弧進行處理,例如:
?/* 如果使用的是非GNU C, 那么就忽略__attribute__ */
#ifndef __GNUC__
? ? ? #define ? ? __attribute__(x) ? ? /* NOTHING * /
#endif
需要說明的是,__attribute__適用于函數(shù)的聲明而不是函數(shù)的定義。所以,當需要使用該屬性的函數(shù)時,必須在同一個文件里進行聲明,例如:
/* 函數(shù)聲明 */
void? die( const char *format, ... ) __attribute__( (noreturn) ) ? __attribute__( ( format(printf,1,2) ) );
void? die( const char *format,... )
{ ? /* 函數(shù)定義 */? }
更多屬性參考:http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html
?
10. 變量屬性(Variable Attributes)
關鍵字__attribute__也可以對變量(variable)或結構體成員(structure field)進行屬性設置。
在使用__attribute__參數(shù)時,你也可以在參數(shù)的前后都加上“__”(兩個下劃線),例如,使用__aligned__而不是aligned,這樣,你就可以在相應的頭文件里使用它而不用關心頭文件里是否有重名的宏定義。
?
11. 類型屬性(Type Attribute)
關鍵字__attribute__也可以對結構體(struct)或共用體(union)進行屬性設置。
大致有六個參數(shù)值可以被設定:aligned,packed,transparent_union,unused,deprecated,may_alias
?
12. aligned (alignment)
該屬性設定一個指定大小的對齊格式(以字節(jié)為單位),例如:
struct S { short f[3]; } __attribute__ ( ( aligned (8) ) );
typedef? int? more_aligned_int __attribute__ ( ( aligned (8) ) );
這里,如果sizeof(short)的大小為2(byte),那么,S的大小就為6。取一個2的次方值,使得該值大于等于6,則該值為8,所以編譯器將設置S類型的對齊方式為8字節(jié)。該聲明將強制編譯器確保(盡它所能)變量類型為struct S或者more-aligned-int的變量在分配空間時采用8字節(jié)對齊方式。
如上所述,你可以手動指定對齊的格式,同樣,你也可以使用默認的對齊方式。例如:
struct S { short f[3]; } __attribute__ ( (aligned) );
上面,aligned后面不緊跟一個指定的數(shù)字值,編譯器將依據(jù)你的目標機器情況使用最大最有益的對齊方式。
int? x __attribute__ ( (aligned (16) ) )? =? 0;
編譯器將以16字節(jié)(注意是字節(jié)byte不是位bit)對齊的方式分配一個變量。也可以對結構體成員變量設置該屬性,例如,創(chuàng)建一個雙字對齊的int對,可以這么寫:
Struct? foo {? int? x[2] __attribute__ ( (aligned (8) ) );? };
選擇針對目標機器最大的對齊方式,可以提高拷貝操作的效率。
aligned屬性使被設置的對象占用更多的空間,相反的,使用packed可以減小對象占用的空間。
需要注意的是,attribute屬性的效力與你的連接器也有關,如果你的連接器最大只支持16字節(jié)對齊,那么你此時定義32字節(jié)對齊也是無濟于事的。
?
13. packed
使用該屬性可以使得變量或者結構體成員使用最小的對齊方式,即對變量是一字節(jié)對齊,對域(field)是位對齊。使用該屬性對struct或者union類型進行定義,設定其類型的每一個變量的內(nèi)存約束。當用在enum類型定義時,暗示了應該使用最小完整的類型 (it indicates that the smallest integral type should be used)。
下面的例子中,x成員變量使用了該屬性,則其值將緊放置在a的后面:
struct? test
{
? ? ? char? a;
? ? ? int? x[2] __attribute__ ((packed));
};
下面的例子中,my-packed-struct類型的變量數(shù)組中的值將會緊緊的靠在一起,但內(nèi)部的成員變量s不會被“pack”,如果希望內(nèi)部的成員變量也被packed,my-unpacked-struct也需要使用packed進行相應的約束。
struct my_packed_struct
{
? ? ? ? char? c;
? ? ? ? int? i;
? ? ? ? struct? my_unpacked_struct? s;
}__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
?
14. 變量屬性與類型屬性舉例
下面的例子中使用__attribute__屬性定義了一些結構體及其變量,并給出了輸出結果和對結果的分析。
程序代碼為:
struct? p
{
?? ? ? int a;
?? ? ? char b;
?? ? ? char c;
}__attribute__( ( aligned(4) ) ) pp;
struct? q
{
?? ? ? int a;
?? ? ? char b;
?? ? ? struct n 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字節(jié)對齊的,所以要在a,b后面添3個空余字節(jié),然后才能存儲qn,
4+1+(3)+8+1=17
因為qq采用的對齊是8字節(jié)對齊,所以qq的大小必定是8的整數(shù)倍,即qq的大小是一個比17大又是8的倍數(shù)的一個最小值,由此得到
17<2^4+8=24= sizeof(qq)
更詳細的介紹見:http://gcc.gnu.org/
下面是一些便捷的連接:
GCC 4.0 Function Attributes?
GCC 4.0 Variable Attributes?
GCC 4.0 Type Attributes?
?
15. Ref
簡單__attribute__介紹:http://www.unixwiz.net/techtips/gnu-c-attributes.html
詳細__attribute__介紹:http://gcc.gnu.org/
總結
以上是生活随笔為你收集整理的__attribute__机制介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一颗颗眼泪都是爱剧情介绍
- 下一篇: gcc -l参数和-L参数