GCC使用__attribute__關鍵字來描述函數,變量和數據類型的屬性,用於編譯器對源代碼的優化。
GCC使用__attribute__關鍵字來描述函數,變量和數據類型的屬性,用於編譯器對源代碼的優化。
描述函數屬性的幾個重要的關鍵字:
void noreturnfun() __attribute__((noreturn));//函數不會返回。
void centon() __attribute__((alias("__centon")));//設置函數別名,函數是__cencon,別名是centon.
void main_enter() __attribute__((constructor));//main_enter函數在進入main函數前調用
void main_exit() __attribute__((destructor));//main_exit函數在main函數返回后調用
void fun() __attribute__ ((noinline));//fun函數不能作為inline函數優化
void fun() __attribute__ ((section("specials”)));//將函數放到specials段中,而不是通常的text段中
no_instrument_function、constructor和destructor關鍵字主要用於剖析(profiling)源代碼的。
在調某個用函數之前和退出某個函數之后調用這些剖析函數,配合addr2line工具可以統計程序的運行狀態。__cyg_profile_func_enter和__cyg_profile_func_exit是GCC指定的進入和返回調用的函數名。配合no_instrument_function關鍵字屬性可以使用它記錄剖析數據,在編譯這樣的代碼的時候,需要在gcc的命令行中加入-finstrument-functions選項,如果要使用addr2line工具分析源代碼,則還要加上-g的gcc命令行選項使得源代碼中的符號可以保留。這2個函數需要2個參數,void *func_address是將要調用的函數地址,void *call_site是調用該函數的地址。
void __cyg_profile_func_enter( void *func_address, void *call_site )
__attribute__ ((no_instrument_function));
void __cyg_profile_func_exit ( void *func_address, void *call_site )
__attribute__ ((no_instrument_function));
constructor和destructo是對main函數做上述剖析的關鍵字,不過這個函數的名稱就可以不特定了,而且這樣的函數沒有參數。如下:
void __main_enter(void) __attribute__ ((constructor));
void __main_exit(void) __attribute__ ((destructor));
描述變量屬性的幾個重要的關鍵字:
int alivalue __attribute__ ((aligned(32)));//變量所存放的內存地址32字節邊界對齊
struct zrecord {
char id;
int zar[32] __attribute__ ((packed));
};//緊湊安排數據結構中的成員元素。如果不使用packed屬性則將zar數組按最小的對齊方式在內存中安排空間,X86平台為4,這樣在id和zar之間將會有3個字節的空洞存在。而使用了packed屬性后,將不會存在這樣的空洞。這次屬性是依賴與硬件平台的。
struct domx __attribute__ ((section(“domx”))) = { 0 };
int trigger __attribute__ ((section(“MONLOG”))) = 0; //不將全局變量放在默認的data或bss段中。而指定特定的段中。
描述數據類型的幾個重要的關鍵字:
struct blockm{
char j[3];
}__attribute__((aligned(32)));//此數據類型的變量的內存地址32字節邊界對齊
復合聲明返回值(Compound Statements Returning a Value):
在一對圓括號中的最后一個表達式所計算的值為返回值。如:
int rslt = ({
int a=5;
a+3;
});//rslt所獲得的返回值為8
這個特性的通常用處可書的P87(文檔P112)參考。
函數參數構造(Function Argument Construction):
GCC內建了3個函數,用於對某一函數的參數構造,調用相關函數,獲得相關函數的返回值。
void *__builtin_apply_args(void);//構造調用此函數的父函數的參數,這些參數都是保存在函數棧(stack)中的。
void *__builtin_apply(void (*func)(), void *arguments, int size);//調用相關函數,第一參數是相關函數的執政,第二個參數是剛才構造參數的函數的返回的指針,第三個參數是建立一個新的棧(stack)而從舊棧中復制數據的尺寸。
__builtin_return(void *result);//獲得相關函數的返回。參數是剛才調用相關函數的函數的返回指針。
如:
#include <stdio.h>
int passthrough();
int average();
int main(int argc,char *argv[])
{
int result;
result = passthrough();
printf(“result=%d\n”,result);
return (0);
}
int passthourgh(int a,int b,int c)
{
void *record;
void *playback;
void (* fn)() = (void (*) ()) average;
record = __builtin_apply_args();
playback = __builtin_apply(fn,record,128);
__builtin_return(playback);
}
int average(int a,int b,int c)
{
Return ((a+b+c)/3;
}
內聯函數(Inline function):
內聯函數在某些情況下類似與宏(macro)。
在一定條件下編譯,內聯函數將直接將代碼內嵌到調用它的父函數中,編譯時指定-O選項才可能被內嵌。也可以指定內嵌函數一個屬性“always_inline”強制內嵌。
有幾種情況將不內嵌,而作為普通函數調用:
1、 不確定數量參數的函數
2、 調用alloca類庫函數的
3、 有可變尺寸數組聲明的。
4、 非本地goto的。
5、 嵌套調用的。
使用ISO C標准的時候,可以使用__inline__關鍵字代替inline關鍵字。
在glib的regex_internal.h文件中有這么幾行:
#ifdef __GNUC__
# define __attribute __attribute__
#else
# define __attribute
#endif
原來兩個是一會事
__attribute__((visibility("default")))
這個關鍵字與gcc編譯參數-fvisibility=hidden
配合使用,如果使用了hidden參數,那么該.so庫所有符號只對內可見,對外不可見,即使鏈接了這個.so庫,還是不能調用其中的符合(函數等);
對於大型工程,為了避免同名符號沖突(默認情況下,先鏈接的.so庫符號會被可執行程序先鏈接,即如果有兩個fun函數,分別在a.so 和 b.so,先鏈接a,則使用a的fun函數),可以先使用-fvisibility=hidden
將所有符號隱藏,然后在需要對外的符號前添加__attribute__((visibility("default")))
屬性來保證符號對外可見。
當然也可以反過來,將-fvisibility
設置為"default"或不設置這個屬性,默認所有符號都對外可見,然后在不對外可見的函數或變量前添加__attribute__((visibility("hidden")))
來隱藏該符號
例如:
extern int my_printf (void *my_object, const char *my_format, ...)
__attribute__ ((format (printf, 2, 3)));
format (printf, 2, 3)告訴編譯器,my_format相當於printf的format,而可變參數是從my_printf的第3個參數開始。
這樣編譯器就會在編譯時用和printf一樣的check法則來確認可變參數是否正確了。