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