gcc 的 __attribute__ 关键字简介

╄→尐↘猪︶ㄣ 提交于 2020-02-01 12:29:07

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;
}

以上仅仅是一些常见的用法。更多的用法,还请参考文首的三篇参考文献。

(完)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!