嵌入式C中__attribute__编译属性说明

锲而不舍,金石可镂

前言

__attribute__是GNU C扩展下一大特性机制,用于设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
__attribute__前后以两个 _ 标识,后端跟 () 说明跟随参数属性。语法格式为:

_ attribute _ (( ATTRIBUTE ))

其中ATTRIBUTE是属性的说明,多个说明之间以逗号分隔。GCC目前可以支持是几个属性,下面介绍常用属性设置。


参数介绍

1、aligned

aligned用于变量、结构或联合,设定一个指定大小的对齐格式,以字节为单位(参数有效值为2的幂值),比如:

struct _array_
{
    char  flag;
    int   state;
    int length;
}__attribute__((aligned(8)));

struct _array_ array_zero;
int main(void)
{
    printf("This struct size is %d\r\n",sizeof(array_zero));
}

输出结果

This struct size is 16

需要注意的是aligned属性使编译器尽其所能的确保在分配变量空间时,为其设置8字节对齐大小。如果属性后不跟指定数字,编译器将依据你的目标机器情况使用最大最有益的对齐方式。

struct _array_
{
    char  array;
    int   flag;
}__attribute__((aligned(2)));

struct _array_ array_zero;
int main(void)
{
    printf("This struct size is %d\r\n",sizeof(array_zero));
}

如上代码,指定对其方式2字节对齐。结构体对齐则为4字节对齐,则实际对齐方式为4字节对齐,编译输出如下:

This struct size is 8

需要注意的是,attribute 属性的效力与你的连接器也有关,如果你的连接器最大只支持16 字节对齐,那么你此时定义32 字节对齐也是无济于事的。


2、packed

属性packed用于变量和类型,用于变量或结构体成员时表示使用最小可能的对齐,用于枚举、结构体或联合类型时表示该类型使用最小的内存。同属性aligned指引字节对齐时,刚好相反。参照aligned代码测试,编写如下测试代码

struct _array_
{
    char  array;
    int   falg;
}__attribute__((packed));

struct _array_ array_zero;
int main(void)
{
    printf("This struct size is %d\r\n",sizeof(array_zero));
}

结构体类型 arraypacked属性修饰后取消字节对齐,成员变量按1字节大小对齐。编译输出如下:

This struct size is 5

其属性作用效果类似于 #pragma pack(1),需要注意的是packed仅对修饰对象起作用,对内嵌结构体不生效。如下所示:

struct  S{
    char eox;
    int  length;
};

struct _array_
{
    char  array;
    int   falg;
    struct S  _S_;
}__attribute__((packed));

struct _array_ array_zero;
int main(void)
{
    printf("This struct size is %d\r\n",sizeof(array_zero));
}

代码中结构体类型 arraypacked属性修饰,成员变量按1字节对齐。但结构体 S 未被修饰,仍按4字节对齐。编译输出如下:

This struct size is 13


3、at

用来设置变量的绝对地址,指定某个变量处于RAM或 FLASH 里面的某个给定的地址,语法为__attribute__((at(addr)))。
1)、定位到flash中,一般用于固化的信息,如出厂设置的参数,上位机配置的参数,ID卡的ID号,flash标记等等
2)、定位到RAM中,一般用于数据量比较大的缓存,如串口的接收缓存,再就是某个位置的特定变量

const uint8_t array[] __attribute__((at(0x08002800)))={0xA5,0x5A};//定位至flash中,地址为0X08002800
uint8_t RecvBuffer[MAX_RECV_LEN] __attribute__ ((at(0x00025000)));	//接收缓冲,最大MAX_RECV_LEN个字节,起始地址为 0x00025000

以定位至flash为例,工程中添加定义代码。编译查看map文件,Global Symbols类别下,查找到array定义地址信息

array 0x08002800 Data 2 main.o(.ARM.__AT_0x08002800)

或者将编译后的二进制文件使用J-FLASH打开,定位至地址0X8002800,参数值已被写入。
在这里插入图片描述
定位至flash中,需要增加const关键字修饰。另外关于定义使用,需要注意以下两点

1、绝对定位不能在函数中定义,局部变量是定义在栈区的,栈区由MDK自动分配、释放,不能定义为绝对地址,只能放在函数外定义。
2、定义的长度不能超过栈或Flash的大小,否则,造成栈、Flash溢出。


4、section

该属性用于修饰函数或者变量,在编译时将被修饰的变量或是函数编译至特定段中。语法使用__attribute__((section(“xxx”)))

uint8_t ret attribute((section(“.test.”))) = 0;

该段语句意为定义uint8_t类型变量,其初值为0,编译放入".test."的输入段中。工程中添入代码,打开map文件,查找对应信息。

ret 0x200000d0 Data 1 main.o(.test.)

或者修饰函数,也是该属性使用最多的场景。一般与其他属性合并使用,用于Initcall机制中。如:

void func_attribute(void)  __attribute__ ((section(".fun.")));

void func_attribute(void) 
{
    DEBUG_log("This is test\r\n");
}

编译打开map文件,查找对应属性表如下:

func_attribute 0x08000161 Thumb Code 10 main.o(.fun.)


总结

__attribute__支持多属性联合使用,括号内以 ( )做分割。一般常见于操作系统中,大部分第一次接触该扩展属性都是基于Linux下Initcall机制,或者RTT中自动初始化机制(Initcall)。有时间写一篇关于Initcall机制介绍的文章,用作记录。
码字不易,如有错漏,敬请指正。一键三连就更好了!(^ _ ^)!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值