rC语言__attribute__的运用

编程语言及工具

102人已加入

描述

在阅读内核源码时,常常可以看到类似于这样子的写法:

static char envval[256] __attribute__((aligned(8)));

即,在某一个结构体完成定义后,跟上一个__attribute__(xxx),这是GNU C的一个特色机制,使用__attribute__可以用来设置函数属性、变量属性和类型属性。

__attribute__的书写特征是在attribute前后都有两个下划线且后面紧跟一对括弧,括弧中包含对应的参数:

__attribute__((attribute-list))

关键字__attribute__可以对函数、变量、类型(包括结构体struct和共用体union)进行属性设置,在使用__attribute__参数时,可以在参数前后也加上双下划线__,效果是会在相应头文件里使用它而不用关心头文件里是否存在重名宏定义。

常见的attribute参数介绍

aligned

指定对象的对齐格式(字节单位),如:

struct S {
  short b[3];
} __attribute__ ((aligned (8)));
 
typedef int int32_t __attribute__ ((aligned (8)));

该声明将强制编译器确保变量类型为Struct S或者int32_t的变量(成员)在分配空间时采用8字节对齐方式。

采用上述格式可以手动指定对齐格式,同样可以采用默认的对齐方式,不指定数字时,编译器将依据目标机器情况使用最大最有益的对齐方式:

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

在上面的例子中,如果一个short大小为2字节,那么S的大小为6字节。取一个大于等于6的2次方值,则该值为8,编译器会将S类型设置为对齐方式8字节, 可以看出aligned属性使被设置的对象占用更多空间

attribute属性效力也受到连接器限制,如果机器最大只支持16字节对齐,设置32并不会有什么用。

下面继续使用一些小例子来观察__attribute__的作用:

struct p
{
    int a; // 4字节
    char b; // 1字节
    short c; // 2字节


}__attribute__((aligned(4))) pp; // 按4对齐,|a |bc |,pp大小8字节


struct m
{
    char a; // 1字节
    int b; // 4字节
    short c; // 2字节
}__attribute__((aligned(4))) mm; // 按4对齐,|a|b|c|,mm大小12字节
 
struct x
{
    int a; // 4字节
    char b; // 1字节
    struct p px; // 8字节
    short c; // 2字节
}__attribute__((aligned(8))) xx; // 按8对齐,|ab|px|c|, 24字节

对齐在N上的概念是指, 某一变量的存放起始地址%N=0 ,编译器对齐原则:

1.数据类型自身的对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。

2.结构体或者类的自身对齐值是其成员中自身对齐值最大的那个值。

3.指定对齐值:是指使用#pragma pack (value)时的指定对齐值value。

4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。

packed

使用该属性对struct和union类型进行定义,设定其类型的每一个变量的内存约束。要求编译器取消结构在编译过程中的优化对齐(按1字节对齐),是GCC特有语法,只跟编译器有关。

struct unpacked_struct
{
      char c;
      int i;
};


struct packed_struct
{
     char c;
     int  i;
     struct unpacked_struct s;
}__attribute__ ((__packed__));

如上面的例子中,packed_struct类型的变量中的成员会紧紧挨在一起,但需要注意其内部unpacked_struct类型的成员变量s的内部不会被pack,如果希望内部成员变量也被packed,对于unpacked_struct也需要使用packed进行约束。

struct my{ char ch; int a;}__attrubte__ ((packed))
sizeof(int)=4;sizeof(my)=5

at

at表示绝对定位,可以把变量或函数绝对定位到Flash中,或者定位到RAM。

1)定位到flash中,一般用于固化的信息,如出厂参数,上位机配置参数,ID卡卡号,flash标记等。

const u16 gFlashDefValue[512] __attribute__((at(0x0800F000))) = {0x1111,0x1111,0x1111,0x0111,0x0111,0x0111};//定位在flash中,其他flash补充为00
const u16 gflashdata__attribute__((at(0x0800F000))) = 0xFFFF;

2)定位到RAM中,一般用于数据量比较大的缓存,如串口缓存,再就是某个位置的特定变量。

u8 USART2_RX_BUF[USART2_REC_LEN] __attribute__ ((at(0X20001000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.

绝对定位不能在函数中定义,局部变量定义在栈区,由MDK(微控制器开发套件)自动分配释放,不能定义为绝对地址,只能在函数外定义;定义长度不能造成堆栈或flash溢出。

section

将作用的函数放入指定段中

4.3.13. __attribute__((section("name")))
The section function attribute enables you to place code in different sections of the image.


Note
This function attribute is a GNU compiler extension that is supported by the ARM compiler.


Example
In the following example, Function_Attributes_section_0 is placed into the RO section new_section rather than .text.


void Function_Attributes_section_0 (void) __attribute__ ((section ("new_section")));
void Function_Attributes_section_0 (void)
{
    static int aStatic =0;
    aStatic++;
}
In the following example, section function attribute overrides the #pragma arm section setting.


#pragma arm section code="foo"
  int f2()
  {
      return 1;
  }                                  // into the 'foo' area


  __attribute__ ((section ("bar"))) int f3()
  {
      return 1;
  }                                  // into the 'bar' area


  int f4()
  {
      return 1;
  }                                  // into the 'foo' area
#pragma arm section

format

使用该属性可以给被声明的函数加上类似于printf和scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。该功能十分有用,尤其是处理一些很难发现的bug。

format的语法格式为:

format (archetype, string-index, first-to-check)
          format属性告诉编译器,按照printf, scanf, strftime或strfmon的参数表格式规则对该函数的参数进行检查。
          “archetype”指定是哪种风格;
          “string-index”指定传入函数的第几个参数是格式化字符串;
          “first-to-check”指定从函数的第几个参数开始按上述规则进行检查。
如:
__attribute__((format(printf,m,n))),按照printf格式,第m个参数为格式化字符串,参数集从第n个开始
__attribute__((format(scanf,m,n))),同上,按照scanf格式


example:
//m=1;n=2,第二个参数开始,填充第一个参数中的format字符串
extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));
//m=2;n=3,第三个参数开始,填充第二个参数中format字符串
extern void myprint(int l,const char *format,...) __attribute__((format(printf,2,3)));
特别需要注意的是,如果myprint是一个函数的成员函数,那m和n需要加1,因为默认第一个参数是隐身的this指针
//m=3;n=4,成员函数的情况
extern void myprint(int l,const char *format,...) __attribute__((format(printf,3,4)));

需要注意的是,__attribute__并不会为你填充字符串,而是需要使用类似于va_list这样的可变参数列表,或者通过参数地址计算去直接处理参数列表,编译器只是会对传入参数做检查,并在启用-Wall选项时输出参数不正确的警告信息。

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

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
因为应该返回的函数却没有返回值,编译产生警告。
如果加上__attribute__
extern void myexit() __attribute__((noreturn));
则不会产生警告。

pure和const

用pure属性修饰的函数用来说明该函数除了返回值之外没有其他任何 效果,并且该函数所返回的值仅仅依赖于函数的形参以及/或全局对象。用 pure属性所修饰的函数可以用来 辅助编译器做消除公共子表达式以及帮助 做循环优化 ,使用这种函数就好比使用算术操作符一般。对同一个使用pure属性修饰的函数连续做两次调用(如果该函数带有参 数,那么两次调用应该用同样的实参),那么这两次调用所返回的结果应 该始终是相同的。const比pure更严格,它要求函数不能读全局对象,此外用const修饰的函数参数不能为指针类型,在const函数内部不能调用非const函数。

always_inline、noinline和flatten

分别为强制优化为内联函数、声明为非内联函数和尽可能做内联处理。

sentinel

提醒程序员此可变参数函数需要一个NULL作为最后一个参数。

used和unused

used告诉编译器避免被连接器因为未被使用而优化掉,unused作用是即使没有使用这个函数,编译器也不警告。

visibility(“visibility_type”)

可见性设置

  • default

default 可见性是默认的符号链接可见性,如果我们不指定visibility 属性,那么默认就使用默认的可见性。默认可见性的对象与函数可以直接在其他模块中引用,包括在动态链接库中 ,它属于一个正常,完整的外部连接。

  • hidden

该符号不存放在动态符号表中,因此,其他可执行文件或共享库都无法直接引用它。使用函数指针可进行间接引用。

  • internal

除非由 特定于处理器的应用二进制接口 (psABI) 指定,否则,内部可见性意味着不允许从另一模块调用该函数。

  • protected

该符号存放在动态符号表中,但定义模块内的引用将与局部符号绑定。也就是说,另一模块无法覆盖该符号。

weak和****weakref

weak声明某一个全局符号为弱符号,当出现重名的时候不引发重定义错误,直接忽略它。weakref为弱引用,功能类似。

可见,__attribute__与编译器密切相关,主要用于编译优化的场景,因为参数实在很多,还有更多的参数并没有再继续列举。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分