在閱讀內(nèi)核源碼時(shí),常常可以看到類似于這樣子的寫法:
static char envval[256] __attribute__((aligned(8)));
即,在某一個(gè)結(jié)構(gòu)體完成定義后,跟上一個(gè)__attribute__(xxx),這是GNU C的一個(gè)特色機(jī)制,使用__attribute__可以用來(lái)設(shè)置函數(shù)屬性、變量屬性和類型屬性。
__attribute__的書寫特征是在attribute前后都有兩個(gè)下劃線且后面緊跟一對(duì)括弧,括弧中包含對(duì)應(yīng)的參數(shù):
__attribute__((attribute-list))
關(guān)鍵字__attribute__可以對(duì)函數(shù)、變量、類型(包括結(jié)構(gòu)體struct和共用體union)進(jìn)行屬性設(shè)置,在使用__attribute__參數(shù)時(shí),可以在參數(shù)前后也加上雙下劃線__,效果是會(huì)在相應(yīng)頭文件里使用它而不用關(guān)心頭文件里是否存在重名宏定義。
常見(jiàn)的attribute參數(shù)介紹
aligned
指定對(duì)象的對(duì)齊格式(字節(jié)單位),如:
struct S {
short b[3];
} __attribute__ ((aligned (8)));
typedef int int32_t __attribute__ ((aligned (8)));
該聲明將強(qiáng)制編譯器確保變量類型為Struct S或者int32_t的變量(成員)在分配空間時(shí)采用8字節(jié)對(duì)齊方式。
采用上述格式可以手動(dòng)指定對(duì)齊格式,同樣可以采用默認(rèn)的對(duì)齊方式,不指定數(shù)字時(shí),編譯器將依據(jù)目標(biāo)機(jī)器情況使用最大最有益的對(duì)齊方式:
struct S {
short b[3];
} __attribute__ ((aligned));
在上面的例子中,如果一個(gè)short大小為2字節(jié),那么S的大小為6字節(jié)。取一個(gè)大于等于6的2次方值,則該值為8,編譯器會(huì)將S類型設(shè)置為對(duì)齊方式8字節(jié), 可以看出aligned屬性使被設(shè)置的對(duì)象占用更多空間 。
attribute屬性效力也受到連接器限制,如果機(jī)器最大只支持16字節(jié)對(duì)齊,設(shè)置32并不會(huì)有什么用。
下面繼續(xù)使用一些小例子來(lái)觀察__attribute__的作用:
struct p
{
int a; // 4字節(jié)
char b; // 1字節(jié)
short c; // 2字節(jié)
}__attribute__((aligned(4))) pp; // 按4對(duì)齊,|a |bc |,pp大小8字節(jié)
struct m
{
char a; // 1字節(jié)
int b; // 4字節(jié)
short c; // 2字節(jié)
}__attribute__((aligned(4))) mm; // 按4對(duì)齊,|a|b|c|,mm大小12字節(jié)
struct x
{
int a; // 4字節(jié)
char b; // 1字節(jié)
struct p px; // 8字節(jié)
short c; // 2字節(jié)
}__attribute__((aligned(8))) xx; // 按8對(duì)齊,|ab|px|c|, 24字節(jié)
對(duì)齊在N上的概念是指, 某一變量的存放起始地址%N=0 ,編譯器對(duì)齊原則:
1.數(shù)據(jù)類型自身的對(duì)齊值:對(duì)于char型數(shù)據(jù),其自身對(duì)齊值為1,對(duì)于short型為2,對(duì)于int,float,double類型,其自身對(duì)齊值為4,單位字節(jié)。
2.結(jié)構(gòu)體或者類的自身對(duì)齊值是其成員中自身對(duì)齊值最大的那個(gè)值。
3.指定對(duì)齊值:是指使用#pragma pack (value)時(shí)的指定對(duì)齊值value。
4.數(shù)據(jù)成員、結(jié)構(gòu)體和類的有效對(duì)齊值:自身對(duì)齊值和指定對(duì)齊值中小的那個(gè)值。
packed
使用該屬性對(duì)struct和union類型進(jìn)行定義,設(shè)定其類型的每一個(gè)變量的內(nèi)存約束。要求編譯器取消結(jié)構(gòu)在編譯過(guò)程中的優(yōu)化對(duì)齊(按1字節(jié)對(duì)齊),是GCC特有語(yǔ)法,只跟編譯器有關(guān)。
struct unpacked_struct
{
char c;
int i;
};
struct packed_struct
{
char c;
int i;
struct unpacked_struct s;
}__attribute__ ((__packed__));
如上面的例子中,packed_struct類型的變量中的成員會(huì)緊緊挨在一起,但需要注意其內(nèi)部unpacked_struct類型的成員變量s的內(nèi)部不會(huì)被pack,如果希望內(nèi)部成員變量也被packed,對(duì)于unpacked_struct也需要使用packed進(jìn)行約束。
struct my{ char ch; int a;}__attrubte__ ((packed))
sizeof(int)=4;sizeof(my)=5
at
at表示絕對(duì)定位,可以把變量或函數(shù)絕對(duì)定位到Flash中,或者定位到RAM。
1)定位到flash中,一般用于固化的信息,如出廠參數(shù),上位機(jī)配置參數(shù),ID卡卡號(hào),flash標(biāo)記等。
const u16 gFlashDefValue[512] __attribute__((at(0x0800F000))) = {0x1111,0x1111,0x1111,0x0111,0x0111,0x0111};//定位在flash中,其他flash補(bǔ)充為00
const u16 gflashdata__attribute__((at(0x0800F000))) = 0xFFFF;
2)定位到RAM中,一般用于數(shù)據(jù)量比較大的緩存,如串口緩存,再就是某個(gè)位置的特定變量。
u8 USART2_RX_BUF[USART2_REC_LEN] __attribute__ ((at(0X20001000)));//接收緩沖,最大USART_REC_LEN個(gè)字節(jié),起始地址為0X20001000.
絕對(duì)定位不能在函數(shù)中定義,局部變量定義在棧區(qū),由MDK(微控制器開(kāi)發(fā)套件)自動(dòng)分配釋放,不能定義為絕對(duì)地址,只能在函數(shù)外定義;定義長(zhǎng)度不能造成堆棧或flash溢出。
section
將作用的函數(shù)放入指定段中
4.3.13. __attribute__((section("name")))
The section function attribute enables you to place code in different sections of the image.
Note
This function attribute is a GNU compiler extension that is supported by the ARM compiler.
Example
In the following example, Function_Attributes_section_0 is placed into the RO section new_section rather than .text.
void Function_Attributes_section_0 (void) __attribute__ ((section ("new_section")));
void Function_Attributes_section_0 (void)
{
static int aStatic =0;
aStatic++;
}
In the following example, section function attribute overrides the #pragma arm section setting.
#pragma arm section code="foo"
int f2()
{
return 1;
} // into the 'foo' area
__attribute__ ((section ("bar"))) int f3()
{
return 1;
} // into the 'bar' area
int f4()
{
return 1;
} // into the 'foo' area
#pragma arm section
format
使用該屬性可以給被聲明的函數(shù)加上類似于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))),按照printf格式,第m個(gè)參數(shù)為格式化字符串,參數(shù)集從第n個(gè)開(kāi)始
__attribute__((format(scanf,m,n))),同上,按照scanf格式
example:
//m=1;n=2,第二個(gè)參數(shù)開(kāi)始,填充第一個(gè)參數(shù)中的format字符串
extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));
//m=2;n=3,第三個(gè)參數(shù)開(kāi)始,填充第二個(gè)參數(shù)中format字符串
extern void myprint(int l,const char *format,...) __attribute__((format(printf,2,3)));
特別需要注意的是,如果myprint是一個(gè)函數(shù)的成員函數(shù),那m和n需要加1,因?yàn)槟J(rèn)第一個(gè)參數(shù)是隱身的this指針
//m=3;n=4,成員函數(shù)的情況
extern void myprint(int l,const char *format,...) __attribute__((format(printf,3,4)));
需要注意的是,__attribute__并不會(huì)為你填充字符串,而是需要使用類似于va_list這樣的可變參數(shù)列表,或者通過(guò)參數(shù)地址計(jì)算去直接處理參數(shù)列表,編譯器只是會(huì)對(duì)傳入?yún)?shù)做檢查,并在啟用-Wall選項(xiàng)時(shí)輸出參數(shù)不正確的警告信息。
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);
}
運(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
noreturn
該參數(shù)告訴編譯器某個(gè)函數(shù)從不返回值,觀察這個(gè)例子:
extern void myexit();
int test(int n){
if(n>0){
myexit();
// 無(wú)返回值
}
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)閼?yīng)該返回的函數(shù)卻沒(méi)有返回值,編譯產(chǎn)生警告。
如果加上__attribute__
extern void myexit() __attribute__((noreturn));
則不會(huì)產(chǎn)生警告。
pure和const
用pure屬性修飾的函數(shù)用來(lái)說(shuō)明該函數(shù)除了返回值之外沒(méi)有其他任何 效果,并且該函數(shù)所返回的值僅僅依賴于函數(shù)的形參以及/或全局對(duì)象。用 pure屬性所修飾的函數(shù)可以用來(lái) 輔助編譯器做消除公共子表達(dá)式以及幫助 做循環(huán)優(yōu)化 ,使用這種函數(shù)就好比使用算術(shù)操作符一般。對(duì)同一個(gè)使用pure屬性修飾的函數(shù)連續(xù)做兩次調(diào)用(如果該函數(shù)帶有參 數(shù),那么兩次調(diào)用應(yīng)該用同樣的實(shí)參),那么這兩次調(diào)用所返回的結(jié)果應(yīng) 該始終是相同的。const比pure更嚴(yán)格,它要求函數(shù)不能讀全局對(duì)象,此外用const修飾的函數(shù)參數(shù)不能為指針類型,在const函數(shù)內(nèi)部不能調(diào)用非const函數(shù)。
always_inline、noinline和flatten
分別為強(qiáng)制優(yōu)化為內(nèi)聯(lián)函數(shù)、聲明為非內(nèi)聯(lián)函數(shù)和盡可能做內(nèi)聯(lián)處理。
sentinel
提醒程序員此可變參數(shù)函數(shù)需要一個(gè)NULL作為最后一個(gè)參數(shù)。
used和unused
used告訴編譯器避免被連接器因?yàn)槲幢皇褂枚鴥?yōu)化掉,unused作用是即使沒(méi)有使用這個(gè)函數(shù),編譯器也不警告。
visibility(“visibility_type”)
可見(jiàn)性設(shè)置
- default
default 可見(jiàn)性是默認(rèn)的符號(hào)鏈接可見(jiàn)性,如果我們不指定visibility 屬性,那么默認(rèn)就使用默認(rèn)的可見(jiàn)性。默認(rèn)可見(jiàn)性的對(duì)象與函數(shù)可以直接在其他模塊中引用,包括在動(dòng)態(tài)鏈接庫(kù)中 ,它屬于一個(gè)正常,完整的外部連接。
- hidden
該符號(hào)不存放在動(dòng)態(tài)符號(hào)表中,因此,其他可執(zhí)行文件或共享庫(kù)都無(wú)法直接引用它。使用函數(shù)指針可進(jìn)行間接引用。
- internal
除非由 特定于處理器的應(yīng)用二進(jìn)制接口 (psABI) 指定,否則,內(nèi)部可見(jiàn)性意味著不允許從另一模塊調(diào)用該函數(shù)。
- protected
該符號(hào)存放在動(dòng)態(tài)符號(hào)表中,但定義模塊內(nèi)的引用將與局部符號(hào)綁定。也就是說(shuō),另一模塊無(wú)法覆蓋該符號(hào)。
weak和****weakref
weak聲明某一個(gè)全局符號(hào)為弱符號(hào),當(dāng)出現(xiàn)重名的時(shí)候不引發(fā)重定義錯(cuò)誤,直接忽略它。weakref為弱引用,功能類似。
可見(jiàn),__attribute__與編譯器密切相關(guān),主要用于編譯優(yōu)化的場(chǎng)景,因?yàn)閰?shù)實(shí)在很多,還有更多的參數(shù)并沒(méi)有再繼續(xù)列舉。
評(píng)論