__attribute__機制介紹

1. __attribute__

GNU C的一大特色(卻不被初學者所知)就是__attribute__機制。

__attribute__可以設置函數屬性(Function Attribute)、變量屬性(Variable Attribute)和類型屬性(Type Attribute)

__attribute__前后都有兩個下劃線,并且后面會緊跟一對原括弧,括弧里面是相應的__attribute__參數

__attribute__語法格式為:

__attribute__ ( ( attribute-list ) )

函數屬性(Function Attribute),函數屬性可以幫助開發者把一些特性添加到函數聲明中,從而可以使編譯器在錯誤檢查方面的功能更強大。

__attribute__機制也很容易同非GNU應用程序做到兼容。

GNU CC需要使用 –Wall,這是控制警告信息的一個很好的方式。下面介紹幾個常見的屬性參數。

?

2. format

該屬性可以使編譯器檢查函數聲明和函數實際調用參數之間的格式化字符串是否匹配。它可以給被聲明的函數加上類似printf或者scanf的特征,該功能十分有用,尤其是處理一些很難發現的bug

format的語法格式為:

format ( archetype,? string-index,? first-to-check )

format屬性告訴編譯器,按照printfscanfstrftimestrfmon的參數表格式規則對該函數的參數進行檢查。archetype:指定是哪種風格;

string-index:指定傳入函數的第幾個參數是格式化字符串;

first-to-check:指定從函數的第幾個參數開始按上述規則進行檢查。

具體使用格式如下:

__attribute__( ( format( printfmn ) ) )

__attribute__( ( format( scanfmn ) ) )

其中參數mn的含義為:

m第幾個參數為格式化字符串(format string);

n參數集合中的第一個,即參數“…”里的第一個參數在函數參數總數排在第幾

注意,有時函數參數里還有隱身的呢,后面會提到;

在使用上,__attribute__((format(printf,m,n)))是常用的,而另一種卻很少見到。

下面舉例說明,其中myprint為自己定義的一個帶有可變參數的函數,其功能類似于printf

//m=1n=2

extern void? myprint( const char *format… ) __attribute__( ( format( printf12 ) ) );

//m=2n=3

extern void? myprint( int lconst char *format... ) __attribute__( ( format( printf23 ) ) );

需要特別注意的是,如果myprint是一個函數的成員函數,那么mn的值可有點懸乎了,例如:

//m=3n=4

extern void? myprint( int lconst char *format... ) __attribute__( ( format( printf34 ) ) );

其原因是,類成員函數的第一個參數實際上一個隱身this指針。(有點C++基礎的都知道點this指針,不知道你在這里還知道嗎?)
這里給出測試用例:attribute.c,代碼如下:

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

如果在attribute.c中的函數聲明去掉__attribute__((format(printf,1,2))),再重新編譯,

既運行$gcc –Wall –c attribute.c attribute后,則并不會輸出任何警告信息。

注意,默認情況下,編譯器是能識別類似printf標準庫函數。

?

3. noreturn

該屬性通知編譯器函數從不返回值。

當遇到函數需要返回值卻還沒運行到返回值處就已退出來的情況,該屬性可以避免出現錯誤信息。C庫函數中的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

警告信息也很好理解,因為你定義了一個有返回值的函數test卻有可能沒有返回值,程序當然不知道怎么辦了!加上__attribute__((noreturn))則可以很好的處理類似這種問題。把extern void myexit();修改為:

extern void? myexit() __attribute__((noreturn));

之后,編譯不會再出現警告信息。

?

4. const

該屬性只能用于帶有數值類型參數的函數上,當重復調用帶有數值參數的函數時,由于返回值是相同的。所以此時編譯器可以進行優化處理,除第一次需要運算外, 其它只需要返回第一次的結果。

該屬性主要適用于沒有靜態狀態(static state)和副作用的一些函數,并且返回值僅僅依賴輸入的參數。為了說明問題,下面舉個非常糟糕的例子,該例子將重復調用一個帶有相同參數值的函數,具體如下:

extern int? square( int? n ) __attribute__ ( (const) );

for (i = 0; i < 100; i++ )? ? ? ? ? ? ? ? ?

{ ? ? ?

?? ? ? total += square (5) + i; ? ? ? ? ? ?

}

添加__attribute__((const))聲明,編譯器只調用了函數一次,以后只是直接得到了相同的一個返回值。

事實上,const參數不能用在帶有指針類型參數的函數中,因為該屬性不但影響函數的參數值,同樣也影響到了參數指向的數據,它可能會對代碼本身產生嚴重甚至是不可恢復的嚴重后果。并且,帶有該屬性的函數不能有任何副作用或者是靜態的狀態,類似getchar()或time()的函數是不適合使用該屬性。

?

5. finstrument-functions

該參數可以使程序在編譯時,在函數的入口和出口處生成instrumentation調用。恰好在函數入口之后并恰好在函數出口之前,將使用當前函數的地址和調用地址來調用下面的profiling函數。(在一些平臺上,__builtin_return_address不能在超過當前函數范圍之外正常工作,所以調用地址信息可能對profiling函數是無效的)

void? __cyg_profile_func_enter( void? *this_fnvoid? *call_site );

void? __cyg_profile_func_exit( void? *this_fnvoid? *call_site );

其中,第一個參數this_fn是當前函數的起始地址,可在符號表中找到;第二個參數call_site是調用處地址。

?

6. instrumentation

也可用于在其它函數中展開的內聯函數。從概念上來說,profiling調用將指出在哪里進入和退出內聯函數。這就意味著這種函數必須具有可尋址形式。如果函數包含內聯,而所有使用到該函數的程序都要把該內聯展開,這會額外地增加代碼長度。如果要在C 代碼中使用extern inline聲明,必須提供這種函數的可尋址形式。
可對函數指定no_instrument_function屬性,在這種情況下不會進行 instrumentation操作。例如,可以在以下情況下使用no_instrument_function屬性:上面列出的profiling函數、高優先級的中斷例程以及任何不能保證profiling正常調用的函數。

no_instrument_function

如果使用了-finstrument-functions,將在絕大多數用戶編譯的函數的入口和出口點調用profiling函數。使用該屬性,將不進行instrument操作。

?

7. constructor/destructor

若函數被設定為constructor屬性,則該函數會在main()函數執行之前被自動的執行。類似的,若函數被設定為destructor屬性,則該函數會在main()函數執行之后或者exit()被調用后被自動的執行。擁有此類屬性的函數經常隱式的用在程序的初始化數據方面,這兩個屬性還沒有在面向對象C中實現。

?

8. 同時使用多個屬性

可以在同一個函數聲明里使用多個__attribute__,并且實際應用中這種情況是十分常見的。使用方式上,你可以選擇兩個單獨的__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)) );

如果帶有該屬性的自定義函數追加到庫的頭文件里,那么所以調用該函數的程序都要做相應的檢查。

?

9. 和非GNU編譯器的兼容性

__attribute__設計的非常巧妙,很容易作到和其它編譯器保持兼容。也就是說,如果工作在其它的非GNU編譯器上,可以很容易的忽略該屬性。即使__attribute__使用了多個參數,也可以很容易的使用一對圓括弧進行處理,例如:

?/* 如果使用的是非GNU C, 那么就忽略__attribute__ */

#ifndef __GNUC__

? ? ? #define ? ? __attribute__(x) ? ? /* NOTHING * /

#endif

需要說明的是,__attribute__適用于函數的聲明而不是函數的定義。所以,當需要使用該屬性的函數時,必須在同一個文件里進行聲明,例如:

/* 函數聲明 */

void? die( const char *format ... ) __attribute__( (noreturn) ) ? __attribute__( ( format(printf12) ) );

void? die( const char *format... )

{ ? /* 函數定義 */? }

更多屬性參考:http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html

?

10. 變量屬性(Variable Attributes

關鍵字__attribute__也可以對變量(variable)或結構體成員(structure field)進行屬性設置。

在使用__attribute__參數時,你也可以在參數的前后都加上“__”(兩個下劃線),例如,使用__aligned__而不是aligned,這樣,你就可以在相應的頭文件里使用它而不用關心頭文件里是否有重名的宏定義。

?

11. 類型屬性(Type Attribute

關鍵字__attribute__也可以對結構體(struct)或共用體(union)進行屬性設置。

大致有六個參數值可以被設定:alignedpackedtransparent_unionunuseddeprecatedmay_alias

?

12. aligned (alignment)

該屬性設定一個指定大小的對齊格式(以字節為單位),例如:

struct S { short f[3]; } __attribute__ ( ( aligned (8) ) );

typedef? int? more_aligned_int __attribute__ ( ( aligned (8) ) );

這里,如果sizeofshort)的大小為2byte),那么,S的大小就為6。取一個2的次方值,使得該值大于等于6,則該值為8,所以編譯器將設置S類型的對齊方式為8字節。該聲明將強制編譯器確保(盡它所能)變量類型為struct S或者more-aligned-int的變量在分配空間時采用8字節對齊方式。

如上所述,你可以手動指定對齊的格式,同樣,你也可以使用默認的對齊方式。例如:

struct S { short f[3]; } __attribute__ ( (aligned) );

上面,aligned后面不緊跟一個指定的數字值,編譯器將依據你的目標機器情況使用最大最有益的對齊方式。

int? x __attribute__ ( (aligned (16) ) )? =? 0;

編譯器將以16字節(注意是字節byte不是位bit)對齊的方式分配一個變量。也可以對結構體成員變量設置該屬性,例如,創建一個雙字對齊的int對,可以這么寫:

Struct? foo {? int? x[2] __attribute__ ( (aligned (8) ) );? };

選擇針對目標機器最大的對齊方式,可以提高拷貝操作的效率。
aligned
屬性使被設置的對象占用更多的空間,相反的,使用packed可以減小對象占用的空間。
需要注意的是,attribute屬性的效力與你的連接器也有關,如果你的連接器最大只支持16字節對齊,那么你此時定義32字節對齊也是無濟于事的。

?

13. packed

使用該屬性可以使得變量或者結構體成員使用最小的對齊方式,即對變量是一字節對齊,對域(field)是位對齊。使用該屬性對struct或者union類型進行定義,設定其類型的每一個變量的內存約束。當用在enum類型定義時,暗示了應該使用最小完整的類型 it indicates that the smallest integral type should be used)。

下面的例子中,x成員變量使用了該屬性,則其值將緊放置在a的后面:

struct? test

{

? ? ? char? a;

? ? ? int? x[2] __attribute__ ((packed));

};

下面的例子中,my-packed-struct類型的變量數組中的值將會緊緊的靠在一起,但內部的成員變量s不會被“pack”,如果希望內部的成員變量也被packedmy-unpacked-struct也需要使用packed進行相應的約束。

struct my_packed_struct

{

? ? ? ? char? c;

? ? ? ? int? i;

? ? ? ? struct? my_unpacked_struct? s;

}__attribute__ ( (__packed__) );

其它可選的屬性值還可以是:cleanupcommonnocommondeprecatedmodesectionsharedtls_modeltransparent_unionunusedvector_sizeweakdllimportdlexport等。

更多詳細參考:http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes

?

14. 變量屬性與類型屬性舉例

下面的例子中使用__attribute__屬性定義了一些結構體及其變量,并給出了輸出結果和對結果的分析。
程序代碼為:

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)=%dsizeof(short)=%dsizeof(char)=%d/n"sizeof(int)sizeof(short)sizeof(char));

?? ? ? printf("pp=%dqq=%d /n" sizeof(pp)sizeof(qq));

?? ? ? return 0;

}

輸出結果:

sizeof(int)=4sizeof(short)=2sizeof(char)=1

pp=8qq=24

分析:

sizeof(pp):

sizeof(a)+ sizeof(b)+ sizeof(c)=4+1+1=6<2^3=8= sizeof(pp)

sizeof(qq):

sizeof(a)+ sizeof(b)=4+1=5

sizeof(qn)=8;

qn是采用8字節對齊的,所以要在ab后面添3個空余字節,然后才能存儲qn

4+1+3+8+1=17

因為qq采用的對齊是8字節對齊,所以qq的大小必定是8的整數倍,即qq的大小是一個比17大又是8的倍數的一個最小值,由此得到

17<2^4+8=24= sizeof(qq)

更詳細的介紹見:http://gcc.gnu.org/

下面是一些便捷的連接:

GCC 4.0 Function Attributes?

GCC 4.0 Variable Attributes?

GCC 4.0 Type Attributes?

?

15. Ref

簡單__attribute__介紹:http://www.unixwiz.net/techtips/gnu-c-attributes.html

詳細__attribute__介紹:http://gcc.gnu.org/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383491.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383491.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383491.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【git】git基本操作命令

1.建立本地倉庫 git config --global user.name "lora" git config --global user.email "xxxgmail.com"2.建立目錄 mkdir xxx3.初始化 cd xxx //進入目錄 git init //初始化4.將代碼上傳至本地緩存區 git add . //上傳全部 git add 文件名 //…

【git】解決gitlab ip更改問題

有時候因為部署gitlab虛擬機的ip發生變化&#xff0c;gitlab的clone地址沒有同時更新到新的ip&#xff0c; 這導致后續clone報錯&#xff0c;解決辦法如下&#xff1a; 進入部署gitlab的主機&#xff1a; sudo vim /opt/gitlab/embedded/service/gitlab-rails/config/gitlab.…

gcc -l參數和-L參數

-l參數就是用來指定程序要鏈接的庫&#xff0c;-l參數緊接著就是庫名&#xff0c;那么庫名跟真正的庫文件名有什么關系呢&#xff1f;就拿數學庫來說&#xff0c;他的庫名是m&#xff0c;他的庫文件名是libm.so&#xff0c;很容易看出&#xff0c;把庫文件名的頭lib和尾.so去掉…

【jenkins】jenkins CI/CD搭建基本過程

1.安裝 &#xff08;1&#xff09;安裝java &#xff08;2&#xff09;安裝jenkins &#xff08;3&#xff09;修改jenkins用戶名密碼配置 &#xff08;4&#xff09;啟動jenkins 2. 插件安裝換源 &#xff08;1&#xff09;插件高級選項換地址 &#xff08;2&#xff09;修改…

apt-get常用命令

一&#xff0c;什么的是apt-get 高級包裝工具&#xff08;英語&#xff1a;Advanced Packaging Tools,簡稱&#xff1a;APT&#xff09;是Debian及其衍生發行版&#xff08;如&#xff1a;ubuntu&#xff09;的軟件包管理器。APT可以自動下載&#xff0c;配置&#xff0c;安裝二…

【jenkins】jenkins build項目的三種方式

jenkins致力于CI/CD&#xff0c; 更改代碼只需要在gitlab push之后&#xff0c;jenkins重新build便可以在tomcat上實現更新部署。 以下為三種構建方式&#xff1a; 1.freestyle project 0. 安裝插件Deploy to container, 并安裝憑證 github連接創建item設置build和post-build …

apt-get 使用詳解

[舉例] 目前常用的 *更新本機中的數據庫緩存&#xff1a; sudo apt-get update *查找包含部分關鍵字的軟件包&#xff1a; sudo apt-cache search <你要查找的name> *安裝指定的軟件&#xff1a; sudo apt-get install <你要安裝的軟件包> *下載軟件包源代碼&…

Buildroot用戶指南

第一章 關于Buildroot Buildroot是一個包含Makefile和修補程序【patch】的集合&#xff0c;這個集合可以使你很容易的為你的目標構建交叉工具鏈【cross-compilationtoolchain】&#xff0c;根文件系統【root filesystem】以及Linux內核映像【kernelimage】。Buildroot可…

【jenkins】jenkins按分支build和email

jenkins按分支build git上傳分支到遠程jenkins打開參數設置更改SCM pipeline代碼&#xff0c;加入branch讀取項 build完發送email 1.安裝插件email extension templates 2.開通發件郵箱smtp服務&#xff0c;配置發件郵箱jenkins&#xff0c;發送測試郵件 3. 項目根目錄添加em…

【c】‘聲明’和malloc不要重復開辟空間

聲明&#xff1a;在內存里開辟一塊連續空間&#xff0c;屬于棧。 malloc&#xff1a;在內存里開辟一塊不連續空間&#xff0c;屬于堆。 //linkedlist.h文件 typedef struct Linkedlist {int data;struct Linkedlist* next&#xff1b; }lnode;//linkedlist.c 比如里邊已經定義…

Buildroot文章翻譯

OpenWRT文章翻譯之&#xff08;一&#xff09;----OpenWRT Buildroot簡介 原文地址&#xff1a;http://wiki.openwrt.org/about/toolchain Buildroot簡介 話說OpenWRT Buildroot 之前&#xff0c;先說一下Buildroot是什么東西吧。Buildroot實際上是一個編譯的腳本&#xff0c…

Buildroot簡介

Buildroot的用法和文檔由Thomas Petazzoni提供&#xff0c;文稿由Karsten Kruse、Ned Ludd、Martin Herren等整理。 最后修改時間&#xff1a;2007-09-19 02:08:10 -0700 (Wed, 19 Sep 2007) l 關于Buildroot l 獲取Buildroot l 使用Buildroot l 定制目標文…

【kali】文件尺寸不符,您使用的鏡像正在同步中

換源時候報錯&#xff1a; vim /etc/apt/sources.list 進去后剛開始添加的阿里源 deb https://mirrors.aliyun.com/kali kali-rolling main non-free contrib deb-src https://mirrors.aliyun.com/kali kali-rolling main non-free contrib執行后報錯 文件尺寸不符&#xff0c…

【kali】kali環境下安裝dvwa

STEP1&#xff1a;從github下載dvwa git clone https://github.com/ethicalhack3r/DVWA Q&#xff1a;我要自己安裝git嗎&#xff1f; A&#xff1a;kali不用啦&#xff0c;一般都自帶有&#xff0c;但是普通的ubuntu和debian上是沒有的哦。 Q&#xff1a;可以換個github網址…

Linux下的C編程實戰之文件系統編程

在Linux平臺下對文件編程可以使用兩類函數&#xff1a;&#xff08;1&#xff09;Linux操作系統文件API&#xff1b;&#xff08;2&#xff09;C語言I/O庫函數。前者依賴于Linux系統調用&#xff0c;后者實際上與操作系統是獨立的&#xff0c;因為在任何操作系統下&#xff0c;…

【kali】kali設置burpsuite抓包dvwa

kali自帶burpsuite 配置代理 burpsuite是通過代理來抓包dvwa的 burpsuite&#xff1a;proxy—> options 里邊監聽的應該是127.0.0.1:8080 &#xff08;端口ip如果撞車了都可以自己改&#xff09; 火狐&#xff1a; preferences–>最下邊的network settings點擊settings —…

fopen函數簡介

函數簡介 函數功能&#xff1a;打開一個文件 函數原型&#xff1a;FILE * fopen(const char * path,const char * mode); 相關函數&#xff1a;open&#xff0c;fclose&#xff0c;fopen_s[1]&#xff0c;_wfopen 所需庫&#xff1a;<stdio.h> 返回值&#xff1a;文件順利…

【kali】kali換了root權限后無法打開firefox瀏覽器

從普通權限換成root權限后發現火狐進不去鳥&#xff01;&#xff01; 終端報錯&#xff1a; Running firefox as root in a regular user’s sessin is not supported.($HOME is /home/miehahaha which is owned by uid 1000) 分析&#xff1a; 是的&#xff0c;原來普通權限m…

【win10】局域網內兩臺win10共享文件夾

https://jingyan.baidu.com/article/93f9803f3e9788e0e46f55c8.html

CentOS 7關閉firewalld啟用iptables

在CentOS7中&#xff0c;有很多CentOS 6中的常用服務發生了變化。 其中iptables是其中比較大的一個。防火墻iptables被firewalld取代。 本文將介紹&#xff0c;如果采用systemctl關閉firewalld&#xff0c;開啟iptables。 1.關閉firewalld [roothwcentos70-01 system]# systemc…