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