來了解GNU C __attribute__

Yunnie
Oct 4, 2023

--

介紹

目前市面上多數的C/C++ IDE所使用的編譯工具鏈都是GCC或LLVM,包含很多的商業IDE的工具鏈也是基於最佳化的GCC或LLVM。而用到一些高階編譯器特性時,我們需要去瞭解一些編譯器指令,例如GNU C的__attribute__,在uboot和Linux原始碼中會常用到此指令。

__attribute__其實是GCC的一種編譯器指令,用來指示編譯器執行實作某些高階操作。__attribute__可以設定函數屬性(Function Attribute)、變數屬性(Variable Attribute)和型別屬性(Type Attribute)。LLVM也藉用了GCC的__attribute__,並進行了擴展。

函數屬性可以幫助開發人員在函數聲明中添加一些特性,這可以使編譯器在錯誤檢查方面增強。變數屬性允許對變數或結構體成員使用特定的屬性進行修飾,例如結構體的對齊控制。

__attribute__的語法格式

__attribute__ ((attribute-list))

attribute的前面和後面都有兩個底線,後面緊跟著兩對元括弧, attribute-list是一個用逗號分隔開的屬性清單。__attribute__ ((attribute-list))放於宣告的尾端「;」之前。

常用屬性

1、packed

讓編譯器在編譯時取消結構體的位元組最佳化對齊,並依照實際佔用的位元組數進行對齊。在某些場景使用者不希望編譯器對位元組對齊進行調整,否則處理起來會比較麻煩,那麼可以使用該屬性。

例如在原始碼中定義了兩個結構

struct unpacked_str
{
uint8_t x;
uint16_t y;
};

struct packed_str
{
uint8_t x;
uint16_t y;
}__attribute__ ((packed));

struct unpacked_str strupkd;
struct packed_str strpkd;

int main(void)
{
printf("%d", sizeof(strupkd));//4
printf("%d", sizeof(strpkd));//3
}

使用clang編譯,運行時分別輸出為4和3。

2、aligned

規定變數或結構體成員最小對齊格式,以位元組為單位。讓使用者自行決定變數的對齊位元組數,例如一些處理器架構要求向量表需要按照規定的對齊位址放置,這時使用者就需要進行控制了。

在程式碼中定義了一個32位元變數:

uint32_t var_in_8bytes __attribute__((aligned(8)));

查看連結後的map映射檔,可以驗證對齊的位元組數和位址。

同樣,你也可以使用預設的對齊方式。如果aligned後面不緊跟著一個指定的數字值,那麼編譯器將依據你的目標機器狀況使用最大、最有益的對齊方式。

struct mystr
{
int16_t a[3];
} __attribute__ ((aligned));

3、section

section控制變數或函數在編譯時的段名。在嵌入式軟體開發時使用的非常多,例如有外擴Flash或RAM時,需要將變數或函數放置到外擴儲存空間,可以在連結腳本中指定段名來操作。在使用MPU(儲存保護)的MCU程式設計時,需要對記憶體劃分區域,將變數或程式碼放置到對應的區域,通常也是透過段操作來實現。

const int identifier[3] __attribute__ ((section ("ident"))) = { 1,2,3 };
void myfunction (void) __attribute__ ((section ("ext_function")))

上述程式碼分別在編譯後,數組和函數所在的段分別為「indent」和「ext_function」。

4、unused

意味著函數或變數很可能未被使用,編譯器不會針對這個函數產生警告,可以將其聲明在函數實作中沒有使用過的參數上,例如:

int main(int argc __attribute__((unused)), char **argv){ …}

5、used

此屬性附加到具有靜態儲存的變量,表示即使變數看起來沒有被引用,也必須保留該變數。否則在連結的時候連結器發現某個變數未被引用,會將此變數優化掉。

6、weak

若兩個或兩個以上全域符號名字一樣,而其中之一則宣告為weak symbol(弱符號),則這些全域符號不會引發重定義錯誤。當普通符號存在時,連結器會忽略掉弱符號,如果不存在普通符號,則使用弱符號。

更多的__attribute__屬性可以參考GCC手冊,在我們需要使用到編譯器一些高階特性的時候,可以在手冊中找到。

--

--