gcc 的 __attribute__ 關鍵字簡介

gcc 的 __attribute__ 關鍵字簡介

參考文檔見:

__attribute__ 可以設置函數屬性,變量屬性,類型屬性。

一、函數屬性

__attribute__可以把一些特性添加到函數聲明中,從而可以使編譯器幫助做更多的錯誤檢查。

  1. __attribute__(format(printf, m, n))
  • m 第m個參數爲格式化字符串
  • n 參數集合中的第一個,即參數"…"裏的第一個參數在函數參數總數排在第n,
    如果是類的成員函數,此值加1,因爲有this指針

一個簡單的示例程序。這個示例程序並沒有什麼用處,僅僅是通過了編譯和運行,證明了語法的正確。

// gcc -Wall test.c 

#include <stdio.h>
#include <stdlib.h>

void myprint(const char *format,...) __attribute__((format(printf,1,2)));

void myprint(const char *format,...)
{
    printf("Hello\n");
}

int main()
{
    int i = 10;
    myprint("i=%d", i);
    return 0;
}
  1. __attribute__(format(scanf, m, n))

語法與上述類似,應用更爲少見。

  1. __attribute__((noreturn))

該屬性通知編譯器該函數不返回值。當遇到某些調用者函數需要返回值卻不可能運行到返回值處就已經退出來的情況,該屬性可以避免出現錯誤信息。C庫函數中的abort()和exit()的聲明格式就採用了這種格式,如下所示:

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

示例程序:

#include <stdio.h>
#include <stdlib.h>

void myexit(int n) __attribute__((noreturn));

void myexit(int n)
{
    printf("I will exit with %d\n", n);
    exit(n);
}

int main()
{
    int i = -1;
    if (i<0) {
        myexit(i);
    }
    else {
        myexit(0);
    }
    printf("Will never hit here\n");
    
    return 0;
}
  1. __attribute__((const))

該屬性的目的是讓編譯器進行優化處理:除第一次需要運算外,其它只需要返回第一次的結果就可以了。
該屬性主要適用於非靜態函數,並且返回值僅僅依賴輸入的參數。

示例程序:

#include <stdio.h>
#include <stdlib.h>

int mycalc(int n) __attribute__((const));
int mycalc2() __attribute__((const));  // not a good example but can pass building

int mycalc(int n)
{
    if (n<-100 || n>100) return 0;
    return n*n;
}

int mycalc2()
{
    return 10;
}

int main()
{
    int i;
    for(i=0; i<10; i++)
    {
        printf("result = %d\n", mycalc(8));
        printf("result = %d\n", mycalc2());
    }
    
    return 0;
}
  1. 同時使用多個屬性

可以在同一個函數聲明裏使用多個__attribute__. 這也是比較常見的。
使用方式上,可以選擇若干個單獨的__attribute__,或者把它們寫在一起,可以參考下面的例子:

// 把打印信息傳遞給stderr然後退出
extern void die(const char *format, ...)
     __attribute__((noreturn))
     __attribute__((format(printf, 1, 2)));

// OR
extern void die(const char *format, ...)
     __attribute__((noreturn, format(printf, 1, 2)));

二、變量屬性

  1. __attribute__(aligned())

此屬性規定變量或結構體成員的最小的對齊格式,以字節爲單位。例如:

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

編譯器將以16字節對齊的方式分配一個變量。
也可以對結構體成員變量設置該屬性,例如,創建一個雙字對齊的int對:

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

如果aligned後面不緊跟一個指定的數字值,那麼編譯器將依據目標機器情況使用最大最有益的對齊方式。例如:

short array[3] __attribute__ ((aligned));

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

示例程序:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int x __attribute__ ((aligned (16))) = 0;
    int y __attribute__ ((aligned (128))) = 0;

    // 不是每個int元素按8字節對齊,
    // 而是2個int元素就按剛好一個8字節對齊,3個int元素就開闢第2個8字節
    struct foo { int x[2] __attribute__ ((aligned (8))); };
    struct foo sf; 
    struct foo3 { int x[3] __attribute__ ((aligned (8))); };
    struct foo4 { int x[4] __attribute__ ((aligned (8))); };
    struct foo5 { int x[5] __attribute__ ((aligned (8))); };

    short array2[2] __attribute__ ((aligned));
    short array3[3] __attribute__ ((aligned));
    short array4[4] __attribute__ ((aligned));
    
    printf("sizeof(x) = %d\n", sizeof(x));      // 4
    printf("sizeof(y) = %d\n", sizeof(y));      // 4
    printf("sizeof(foo) = %d\n", sizeof(struct foo));     // 8
    printf("sizeof(foo.x) = %d\n", sizeof(sf.x));         // 8
    printf("sizeof(foo3) = %d\n", sizeof(struct foo3));  // 16
    printf("sizeof(foo4) = %d\n", sizeof(struct foo4));  // 16
    printf("sizeof(foo5) = %d\n", sizeof(struct foo5));  // 24
    printf("sizeof(short) = %d\n", sizeof(short));    // 2
    printf("sizeof(array2) = %d\n", sizeof(array2));  // 4
    printf("sizeof(array3) = %d\n", sizeof(array3));  // 6
    printf("sizeof(array4) = %d\n", sizeof(array4));  // 8
    
    return 0;
}
  1. __attribute__((packed))

該屬性令變量或結構體成員使用最小的對齊方式,即對變量是一字節對齊,對域(field)是位對齊。

struct mypack   // size is 9=1+4+4
{
  char a;
  int x[2] __attribute__ ((packed));
};
  1. 其他的屬性值: (細節見文首的參考文獻)
  • cleanup
  • common
  • nocommon
  • deprecated
  • mode
  • section
  • shared
  • tls_model
  • transparent_union
  • unused
  • vector_size
  • weak
  • dllimport
  • dlexport

三、類型屬性

關鍵字__attribute__也可以對結構體(struct)或共用體(union)進行屬性設置。
大致有六個參數值可以被設定,即:

  • aligned
  • packed
  • transparent_union
  • unused
  • deprecated
  • may_alias
  1. __attribute__((aligned(alignment)))

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

struct S1 { short f[3]; } __attribute__ ((aligned (8)));
typedef int more_aligned_int __attribute__ ((aligned (8)));

該聲明將強制編譯器確保(盡它所能)變量類型爲struct S或者more-aligned-int的變量在分配空間時採用8字節對齊方式。
同樣,也可以使用默認的對齊方式。如果aligned後面不跟任何數值,那麼編譯器將依據目標機器使用最大最有益的對齊方式。例如:

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

示例程序:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    struct S1 { short f[3]; } __attribute__ ((aligned (8)));
    struct S2 { short f[3]; } __attribute__ ((aligned));
    typedef int more_aligned_int __attribute__ ((aligned (8)));

    int array[3];
    // more_aligned_int array[1]; // compilation error: alignment of array elements is greater than element size

    printf("sizeof(S1)=%d\n", sizeof(struct S1));    // 8
    printf("sizeof(S2)=%d\n", sizeof(struct S2));    // 16
    printf("sizeof(more_aligned_int)=%d\n", sizeof(more_aligned_int)); // 4
    printf("sizeof(array)=%d\n", sizeof(array));     // 12
    printf("sizeof(long)=%d\n", sizeof(long));       // 8

    return 0;
}
  1. __attribute__((packed))

使用該屬性對struct或者union類型進行定義,設定其類型的每一個變量的內存約束。

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

示例程序:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    struct my_unpacked_struct   // size is 8=4+4
    {
        char c;
        int i;
    };
     
    struct my_packed_struct  // size is 13=1+4+8
    {
      char c;
      int i;
      struct my_unpacked_struct s;
    }__attribute__ ((__packed__));
    
    struct my_packed_struct2    // size is 5=1+4
    {
      char c;
      int i;
    }__attribute__ ((__packed__));
    
    printf("sizeof(my_unpacked_struct)=%d\n", sizeof(struct my_unpacked_struct));
    printf("sizeof(my_packed_struct)=%d\n", sizeof(struct my_packed_struct));
    printf("sizeof(my_packed_struct2)=%d\n", sizeof(struct my_packed_struct2));

    return 0;
}

以上僅僅是一些常見的用法。更多的用法,還請參考文首的三篇參考文獻。

(完)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章