GNU __attribute__淺析

GNU __attribute__淺析

1 介紹

__attribute__是GCC的特性,其實是個編譯器指令,告訴編譯器聲明的特性,或者讓編譯器進行更多的錯誤檢查和高級優化。attribute 可以設置函數屬性(Function Attribute)、變量屬性(Variable Attribute)和類型屬性(Type Attribute)。

2 語法

其位置約束爲: 放於聲明的尾部“;” 之前。
attribute 書寫特徵爲: attribute 前後都有兩個下劃線,並切後面會緊跟一對原括弧,括弧裏面是相應的__attribute__ 參數。
attribute 語法格式爲: attribute ((attribute-list))
當__attribute__ 用於修飾對象時,它就如同C 語言語法體系結構的類型限定符,跟const , volatile , restrict 等屬一類。
當__attribute__ 用於修飾函數時,它就相當於一個函數說明符,跟inline,Noreturn 屬同一類。
當__attribute_ 用於修飾一個結構體,聯合體或者枚舉類型,該限定符只能放在類型標識符之前。

3 功能

當我們需要識別當前編譯器能否支持GNU 語法拓展,我們可以使用 __GNU __ 宏作爲區分。
在這裏插入圖片描述

4 常用函數屬性

4.1 noreturn

表示沒有返回值。
這個屬性告訴編譯器函數不會返回,這可以用來抑制關於未達到代碼路徑的錯誤。 C庫函數abort()和exit()都使用此屬性聲明:

extern void exit(int)   __attribute__((noreturn));
extern void abort(void) __attribute__((noreturn));

4.2 always_inline 與noinline

對於聲明爲內聯的函數,會強制優化。所有加了attribute((always_inline))的函數再被調用時不會被編譯成函數調用而是直接擴展到調用函數體內。
noinline與上面的相反,聲明爲非內聯函數。

4.3 flatten

用此修飾的函數,在函數中調用的每一個函數都將盡可能地做內聯處理。而用flatten 屬性修飾的函數,是否內聯處理,就要根據編譯器當前的編譯選項以及當前上下文來定。

4.4 pure

用pure屬性修飾的函數用來說明該函數除了返回值之外沒有其他任何 效果,並且該函數所返回的值僅僅依賴於函數的形參以及/或全局對象。用 pure屬性所修飾的函數可以用來輔助編譯器做消除公共子表達式以及幫助 做循環優化,使用這種函數就好比使用算術操作符一般。
用pure屬性所修飾的函數體內不應該含有無限循環,不應該對volatile 修飾的全局對象進行訪問或是對多個線程所共享的全局對象進行訪問,也 不應該訪問其他系統資源,比如對文件、套接字等進行操作。簡而言之, 對同一個使用pure屬性修飾的函數連續做兩次調用(如果該函數帶有參 數,那麼兩次調用應該用同樣的實參),那麼這兩次調用所返回的結果應 該始終是相同的。因此,用pure屬性所修飾的函數也很容易讓編譯器做內 聯處理。

4.5 const

用const屬性修飾的函數與用pure屬性修飾的十分類似,不過const屬性比pure更嚴格,它要求函數不能讀全局對象。此外,用const屬性修飾的函數的參數不能是一個指針類型,而且在用const屬性修飾的函數內往往不能調用一個非const屬性的函數。

4.6 sentinel

提醒程序員“此可變參數函數需要一個NULL作爲最後一個參數。

4.7 format

attribute format屬性可以給被聲明的函數加上類似printf或者scanf的特徵,它可以使編譯器檢查函數聲明和函數實際調用參數之間的格式化字符串是否匹配。format屬性告訴編譯器,按照printf, scanf等標準C函數參數格式規則對該函數的參數進行檢查。這在我們自己封裝調試信息的接口時非常的有用。

4.8 section(“name”)

attribute((section(“name”))) 其作用是將作用的函數或數據放入指定名爲"section_name"對應的段中。

4.9 unused 與 used

attribute((unused)) 其作用是即使沒有使用這個函數,編譯器也不警告。
attribute((used)) 其作用是告訴編譯器避免被鏈接器因爲未用過而被優化掉。

4.10 weak 與 weakref

什麼是弱符號?
若兩個或兩個以上全局符號(函數或變量名)名字一樣,而其中之一聲明爲weak symbol(弱符號),則這些全局符號不會引發重定義錯誤。鏈接器會忽略弱符號,去使用普通的全局符號來解析所有對這些符號的引用,但當普通的全局符號不可用時,鏈接器會使用弱符號。當有函數或變量名可能被用戶覆蓋時,該函數或變量名可以聲明爲一個弱符號。弱符號也稱爲weak alias(弱別名)。
attribute((weakref))爲弱引用,請注意引用與定義的區別。weakref就是申明某個引用爲弱引用,弱引用時如果需引用符號不存在也不會鏈接出錯,而是將需要引用的符號定義爲WEAK屬性及0地址(跟前面的WEAK屬性很相似吧)。
weakref的用法有點特別,必須要配合alias使用及必須是static定義。attribute((weak(“target”)))相當於__attribute__((weakref,alias(“target”)))。

5 常用結構體屬性

5.1 aligned

attribute((aligned (n))),讓所作用的結構成員對齊在n字節自然邊界上。如果結構中有成員的長度大於n,則按照最大成員的長度來對齊。例如:
不加修飾的情況:

typedef struct
{
    char  member1;
    int   member2;
    short member3;
}Family;
//輸出字節:
NSLog(@"Family size is %zd",sizeof(Family));
//輸出結果爲:
2020-06-02 10:28:45.380 Study[917:436064] Family size is 12

修改字節對齊爲1:

typedef struct
{
    char  member1;
    int   member2;
    short member3;
}__attribute__ ((aligned (1))) Family;
//輸出字節:
NSLog(@"Family size is %zd",sizeof(Family));
//輸出結果爲:
2020-06-02 10:28:05.315 Study[914:435764] Family size is 12

和上面的結果一致,因爲 設定的字節對齊爲1.而結構體中成員的最大字節數是int 4個字節,1 < 4,按照4字節對齊,和系統默認一致。

修改字節對齊爲8:

typedef struct
{
    char  member1;
    int   member2;
    short member3;
}__attribute__ ((aligned (8))) Family;
//輸出字節:
NSLog(@"Family size is %zd",sizeof(Family));
//輸出結果爲:
2020-06-02 10:28:05.315 Study[914:435764] Family size is 16
這裏 8 > 4,按照8字節對齊,結果爲16。

5.2 packed

讓指定的結構結構體按照一字節對齊,測試:
//不加packed修飾

typedef struct {
    char    version;
    int16_t sid;
    int32_t len;
    int64_t time;
} Header;
//計算長度
NSLog(@"size is %zd",sizeof(Header));
輸出結果爲:
2020-06-02 11:53:47.728 Study[14378:5523450] size is 16
可以看出,默認系統是按照4字節對齊。

//加packed修飾

typedef struct {
    char    version;
    int16_t sid;
    int32_t len;
    int64_t time;
}__attribute__ ((packed)) Header;
//計算長度
NSLog(@"size is %zd",sizeof(Header));
輸出結果爲:
2020-06-02 11:57:46.970 Study[14382:5524502] size is 15

6 常用變量屬性

6.1 aligned

指定變量地址對齊。如下:

6.2 section(“name”)

attribute((section(“name”))) 其作用是將作用的函數或數據放入指定名爲"section_name"對應的段中。

6.3 at

__attribute__((at(0xC000_8000))) 其作用是將作用的函數或數據放入指定的地址。

6.4 多種屬性疊加配置方法

static u32_t __mmu_ttb[4096] __attribute__((section(".mmu_ttb"), aligned(0x4000)));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章