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

猜你喜欢

转载自blog.csdn.net/u013836909/article/details/106528152
GNU