__attribute__ 详解
GNU C的一大特色(卻不被初學(xué)者所知)就是__attribute__機(jī)制。__attribute__可以設(shè)置函數(shù)屬性(Function?? ?Attribute)、變量屬性(Variable Attribute)和類型屬性(Type Attribute)。
__attribute__書寫特征是:__attribute__前后都有兩個下劃線,并切后面會緊跟一對原括弧,括弧里面是相應(yīng)的__attribute__參數(shù)。
__attribute__語法格式為:
__attribute__?((attribute-list))
其位置約束為:
放于聲明的尾部“;”之前。
函數(shù)屬性(Function Attribute)
函數(shù)屬性可以幫助開發(fā)者把一些特性添加到函數(shù)聲明中,從而可以使編譯器在錯誤檢查方面的功能更強(qiáng)大。__attribute__機(jī)制也很容易同非GNU應(yīng)用程序做到兼容之功效。
GNU CC需要使用?–Wall編譯器來擊活該功能,這是控制警告信息的一個很好的方式。下面介紹幾個常見的屬性參數(shù)。
__attribute__?format
該__attribute__屬性可以給被聲明的函數(shù)加上類似printf或者scanf的特征,它可以使編譯器檢查函數(shù)聲明和函數(shù)實(shí)際調(diào)用參數(shù)之間的格式化字符串是否匹配。該功能十分有用,尤其是處理一些很難發(fā)現(xiàn)的bug。
format的語法格式為:
format (archetype, string-index, first-to-check)
format屬性告訴編譯器,按照printf, scanf,?
strftime或strfmon的參數(shù)表格式規(guī)則對該函數(shù)的參數(shù)進(jìn)行檢查。“archetype”指定是哪種風(fēng)格;“string-index”指定傳入函數(shù)的第幾個參數(shù)是格式化字符串;“first-to-check”指定從函數(shù)的第幾個參數(shù)開始按上述規(guī)則進(jìn)行檢查。
具體使用格式如下:
__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的值可有點(diǎn)“懸乎”了,例如:
//m=3;n=4
extern?void?myprint(int?l,const?char?*format,...)?
__attribute__((format(printf,3,4)));
其原因是,類成員函數(shù)的第一個參數(shù)實(shí)際上一個“隱身”的“this”指針。(有點(diǎn)C++基礎(chǔ)的都知道點(diǎn)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\n",6);
? ??7:?? ??myprint("i=%s\n",6);
? ??8:?? ??myprint("i=%s\n","abc");
? ??9:?? ??myprint("%s,%d,%d\n",1,2);
? ??10:}
運(yùn)行$gcc –Wall –c attribute.c attribute后,輸出結(jié)果為:
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))),再重新編譯,既運(yùn)行$gcc –Wall –c attribute.c attribute后,則并不會輸出任何警告信息。
注意,默認(rèn)情況下,編譯器是能識別類似printf的“標(biāo)準(zhǔn)”庫函數(shù)。
__attribute__?noreturn
該屬性通知編譯器函數(shù)從不返回值,當(dāng)遇到類似函數(shù)需要返回值而卻不可能運(yùn)行到返回值處就已經(jīng)退出來的情況,該屬性可以避免出現(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
警告信息也很好理解,因?yàn)槟愣x了一個有返回值的函數(shù)test卻有可能沒有返回值,程序當(dāng)然不知道怎么辦了!
加上__attribute__((noreturn))則可以很好的處理類似這種問題。把
extern?void?myexit();修改為:
extern?void?myexit()?__attribute__((noreturn));之后,編譯不會再出現(xiàn)警告信息。
__attribute__?const
該屬性只能用于帶有數(shù)值類型參數(shù)的函數(shù)上。當(dāng)重復(fù)調(diào)用帶有數(shù)值參數(shù)的函數(shù)時,由于返回值是相同的,所以此時編譯器可以進(jìn)行優(yōu)化處理,除第一次需要運(yùn)算外,?其它只需要返回第一次的結(jié)果就可以了,進(jìn)而可以提高效率。該屬性主要適用于沒有靜態(tài)狀態(tài)(static?state)和副作用的一些函數(shù),并且返回值僅僅依賴輸入的參數(shù)。
為了說明問題,下面舉個非常“糟糕”的例子,該例子將重復(fù)調(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ù)一次,以后只是直接得到了相同的一個返回值。
事實(shí)上,const參數(shù)不能用在帶有指針類型參數(shù)的函數(shù)中,因?yàn)樵搶傩圆坏绊懞瘮?shù)的參數(shù)值,同樣也影響到了參數(shù)指向的數(shù)據(jù),它可能會對代碼本身產(chǎn)生嚴(yán)重甚至是不可恢復(fù)的嚴(yán)重后果。
并且,帶有該屬性的函數(shù)不能有任何副作用或者是靜態(tài)的狀態(tài),所以,類似getchar()或time()的函數(shù)是不適合使用該屬性的。
-finstrument-functions
該參數(shù)可以使程序在編譯時,在函數(shù)的入口和出口處生成instrumentation調(diào)用。恰好在函數(shù)入口之后并恰好在函數(shù)出口之前,將使用當(dāng)前函數(shù)的地址和調(diào)用地址來調(diào)用下面的
profiling?
函數(shù)。(在一些平臺上,__builtin_return_address不能在超過當(dāng)前函數(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是當(dāng)前函數(shù)的起始地址,可在符號表中找到;第二個參數(shù)call_site是指調(diào)用處地址。
instrumentation?
也可用于在其它函數(shù)中展開的內(nèi)聯(lián)函數(shù)。從概念上來說,profiling調(diào)用將指出在哪里進(jìn)入和退出內(nèi)聯(lián)函數(shù)。這就意味著這種函數(shù)必須具有可尋址形式。如?果函數(shù)包含內(nèi)聯(lián),而所有使用到該函數(shù)的程序都要把該內(nèi)聯(lián)展開,這會額外地增加代碼長度。如果要在C代碼中使用extern?inline聲明,必須提供這種函數(shù)的可尋址形式。
可對函數(shù)指定no_instrument_function屬性,在這種情況下不會進(jìn)行?Instrumentation操作。例如,可以在以下情況下使用no_instrument_function屬性:上面列出的profiling函?數(shù)、高優(yōu)先級的中斷例程以及任何不能保證profiling正常調(diào)用的函數(shù)。
no_instrument_function
如果使用了-finstrument-functions?
,將在絕大多數(shù)用戶編譯的函數(shù)的入口和出口點(diǎn)調(diào)用profiling函數(shù)。使用該屬性,將不進(jìn)行instrument操作。
constructor/destructor
若函數(shù)被設(shè)定為constructor屬性,則該函數(shù)會在main()函數(shù)執(zhí)行之前被自動的執(zhí)行。類似的,若函數(shù)被設(shè)定為destructor屬性,則該?函數(shù)會在main()函數(shù)執(zhí)行之后或者exit()被調(diào)用后被自動的執(zhí)行。擁有此類屬性的函數(shù)經(jīng)常隱式的用在程序的初始化數(shù)據(jù)方面。
這兩個屬性還沒有在面向?qū)ο?span style="color:#cb2c1e; font:11px Menlo">C中實(shí)現(xiàn)。
同時使用多個屬性
可以在同一個函數(shù)聲明里使用多個__attribute__,并且實(shí)際應(yīng)用中這種情況是十分常見的。使用方式上,你可以選擇兩個單獨(dú)的__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ù)的程序都要做相應(yīng)的檢查。
和非GNU編譯器的兼容性
慶幸的是,__attribute__設(shè)計(jì)的非常巧妙,很容易作到和其它編譯器保持兼容,也就是說,如果工作在其它的非GNU編譯器上,可以很容易的忽略該屬性。即使__attribute__使用了多個參數(shù),也可以很容易的使用一對圓括弧進(jìn)行處理,例如:
#ifndef __GNUC__#?? ??define?? ??__attribute__(x)?? ??#endif?
需要說明的是,__attribute__適用于函數(shù)的聲明而不是函數(shù)的定義。所以,當(dāng)需要使用該屬性的函數(shù)時,必須在同一個文件里進(jìn)行聲明,例如:
void?die(const?char?*format, ...)?__attribute__((noreturn))?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?__attribute__((format(printf,1,2)));?void?die(const?char?*format, ...){? ? ? ? ? ? ? ? ??}?更多的屬性含義參考:http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html?變量屬性(Variable Attributes)
關(guān)鍵字__attribute__也可以對變量(variable)或結(jié)構(gòu)體成員(structure?
field)進(jìn)行屬性設(shè)置。這里給出幾個常用的參數(shù)的解釋,更多的參數(shù)可參考本文給出的連接。
在使用__attribute__參數(shù)時,你也可以在參數(shù)的前后都加上“__”(兩個下劃線),例如,使用__aligned__而不是aligned,這樣,你就可以在相應(yīng)的頭文件里使用它而不用關(guān)心頭文件里是否有重名的宏定義。
aligned (alignment)
該屬性規(guī)定變量或結(jié)構(gòu)體成員的最小的對齊格式,以字節(jié)為單位。例如:
int?x?__attribute__?((aligned (16))) =?0;?編譯器將以16字節(jié)(注意是字節(jié)byte不是位bit)對齊的方式分配一個變量。也可以對結(jié)構(gòu)體成員變量設(shè)置該屬性,例如,創(chuàng)建一個雙字對齊的int對,可以這么寫:
struct?foo {?int?x[2]?__attribute__?((aligned (8))); };?如上所述,你可以手動指定對齊的格式,同樣,你也可以使用默認(rèn)的對齊方式。如果aligned后面不緊跟一個指定的數(shù)字值,那么編譯器將依據(jù)你的目標(biāo)機(jī)器?情況使用最大最有益的對齊方式。例如:
short?array[3]?__attribute__?((aligned));?選擇針對目標(biāo)機(jī)器最大的對齊方式,可以提高拷貝操作的效率。
aligned屬性使被設(shè)置的對象占用更多的空間,相反的,使用packed可以減小對象占用的空間。
需要注意的是,attribute屬性的效力與你的連接器也有關(guān),如果你的連接器最大只支持16字節(jié)對齊,那么你此時定義32字節(jié)對齊也是無濟(jì)于事的。
packed
使用該屬性可以使得變量或者結(jié)構(gòu)體成員使用最小的對齊方式,即對變量是一字節(jié)對齊,對域(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等,
詳細(xì)信息可參考:
http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes
類型屬性(Type Attribute)
關(guān)鍵字__attribute__也可以對結(jié)構(gòu)體(struct)或共用體(union)進(jìn)行屬性設(shè)置。大致有六個參數(shù)值可以被設(shè)定,即:aligned,?
packed, transparent_union, unused, deprecated?和?may_alias。
在使用__attribute__參數(shù)時,你也可以在參數(shù)的前后都加上“__”(兩個下劃線),例如,使用__aligned__而不是aligned,這樣,你就可以在相應(yīng)的頭文件里使用它而不用關(guān)心頭文件里是否有重名的宏定義。
aligned (alignment)
該屬性設(shè)定一個指定大小的對齊格式(以字節(jié)為單位),例如:
struct?S {?short?f[3]; }?__attribute__?((aligned (8)));
typedef?int?more_aligned_int?__attribute__?((aligned (8)));
該聲明將強(qiáng)制編譯器確保(盡它所能)變量類型為struct S或者more-aligned-int的變量在分配空間時采用8字節(jié)對齊方式。
如上所述,你可以手動指定對齊的格式,同樣,你也可以使用默認(rèn)的對齊方式。如果aligned后面不緊跟一個指定的數(shù)字值,那么編譯器將依據(jù)你的目標(biāo)機(jī)器情況使用最大最有益的對齊方式。例如:
struct?S {?short?f[3]; }?__attribute__?((aligned));
這里,如果sizeof(short)的大小為2(byte),那么,S的大小就為6。取一個2的次方值,使得該值大于等于6,則該值為8,所以編譯器將設(shè)置S類型的對齊方式為8字節(jié)。
aligned屬性使被設(shè)置的對象占用更多的空間,相反的,使用packed可以減小對象占用的空間。
需要注意的是,attribute屬性的效力與你的連接器也有關(guān),如果你的連接器最大只支持16字節(jié)對齊,那么你此時定義32字節(jié)對齊也是無濟(jì)于事的。
packed
使用該屬性對struct或者union類型進(jìn)行定義,設(shè)定其類型的每一個變量的內(nèi)存約束。當(dāng)用在enum類型定義時,暗示了應(yīng)該使用最小完整的類型?(it indicates that the smallest integral type should be used)。
下面的例子中,my-packed-struct類型的變量數(shù)組中的值將會緊緊的靠在一起,但內(nèi)部的成員變量s不會被“pack”,如果希望內(nèi)部的成員變量也被packed的話,my-unpacked-struct也需要使用packed進(jìn)行相應(yīng)的約束。
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__屬性定義了一些結(jié)構(gòu)體及其變量,并給出了輸出結(jié)果和對結(jié)果的分析。
程序代碼為:
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;
}
輸出結(jié)果:
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<23=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
因?yàn)?span style="color:#cb2c1e; font:11px Menlo">qq采用的對齊是8字節(jié)對齊,所以qq的大小必定是8的整數(shù)倍,即qq的大小是一個比17大又是8的倍數(shù)的一個最小值,由此得到
17<24+8=24=?sizeof(qq)
更詳細(xì)的介紹見: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.有關(guān)__attribute__的相對簡單的介紹:http://www.unixwiz.net/techtips/gnu-c-attributes.html
2.__attribute__詳細(xì)介紹:http://gcc.gnu.org/
6.7 C++-Specific Variable, Function, and Type Attributes
?
Some attributes only make sense?for?C++ programs.
init_priority (priority)
In Standard C++, objects defined at namespace scope are guaranteed to be initialized?in?an order?instrict accordance with that of their definitions?in?a given translation unit. No guarantee is madefor?initializations across translation units. However, GNU C++ allows users to control the order of initialization of objects defined at namespace scope with the init_priority attribute by specifying a relative priority, a constant integral expression currently bounded between?101?and?65535inclusive. Lower numbers indicate a higher priority.
In the following example, A would normally be created before B, but the init_priority attribute has reversed that order:
Some_Class A?__attribute__?((init_priority (2000)));
Some_Class B?__attribute__?((init_priority (543)));
Note that the particular values of priority?do?not matter; only their relative ordering.
java_interface
This type attribute informs C++ that the class is a Java interface. It may only be applied to classes declared within an?extern?"Java"?block. Calls to methods declared?in?this interface will be dispatched using GCJ's interface table mechanism, instead of regular virtual table dispatch.
總結(jié)
以上是生活随笔為你收集整理的__attribute__ 详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网络游戏服务器架构
- 下一篇: TensorFlow(2)-训练数据载入