锲而不舍,金石可镂
前言
__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));
}
结构体类型 array 被packed属性修饰后取消字节对齐,成员变量按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));
}
代码中结构体类型 array 被packed属性修饰,成员变量按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机制介绍的文章,用作记录。
码字不易,如有错漏,敬请指正。一键三连就更好了!(^ _ ^)!