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

文章详细介绍了GCC的__attribute__扩展,包括用于设置变量、结构或联合对齐的`aligned`,最小内存对齐的`packed`,变量绝对地址的`at`以及将函数或变量放入特定段的`section`。这些属性常用于操作系统和初始化机制中,如Linux的Initcall。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

锲而不舍,金石可镂

前言

__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机制介绍的文章,用作记录。
码字不易,如有错漏,敬请指正。一键三连就更好了!(^ _ ^)!

### 关于 `___attribute__` 的用法和含义 在 C 和 C++ 中,`___attribute__` 是一种 GNU 扩展语法,用于向编译器提供额外的信息或修改某些行为。它通常由 GCC 编译器支持,并允许开发者通过指定属性来优化代码性能、改进调试体验以及增强程序的安全性。 #### 属性的基本结构 `___attribute__` 的基本形式如下: ```c ___attribute__((attribute_list)) ``` 其中 `attribute_list` 表示一系列逗号分隔的属性名称及其参数。这些属性会影响声明的对象(如变量、函数或类型)的行为。 --- #### 常见用途及例子 1. **函数属性** 函数可以通过 `___attribute__` 来定义特定的行为。例如,标记某个函数不会返回 (`noreturn`) 或者总是内联展开 (`always_inline`)。 ```c void my_exit_function(void) ___attribute__((noreturn)); ``` 上述代码表示该函数一旦执行就不会再返回到调用处[^5]。 2. **变量属性** 变量也可以应用属性以改变其存储方式或其他特性。比如使用 `aligned` 属性可以让数据按照指定字节边界对齐: ```c int data ___attribute__((aligned(16))); ``` 这里指定了 `data` 应当按 16 字节对齐[^6]。 3. **类型属性** 对自定义的数据类型设置特殊处理规则也是可能的。例如创建一个新的整数类型并强制对其成员进行严格别名检查(`may_alias`): ```c typedef int new_int_type ___attribute__((may_alias)); ``` 此操作使得即使违反了严格的类型匹配原则也不会引发未定义行为[^7]。 4. **段落属性 (Section Attribute)** 将全局对象放置在一个特别命名的部分中对于嵌入式开发尤其有用: ```c int special_var ___attribute__((section(".my_section"))); ``` 把变量放入名为 `.my_section` 的部分有助于管理内存布局或者与其他硬件资源交互[^8]。 --- #### 注意事项 尽管 `___attribute__` 提供了许多强大的功能,但在实际项目中应当谨慎使用它们。因为这属于非标准扩展,在跨平台移植时可能会遇到兼容性问题。如果目标环境不支持相同的编译选项,则需要考虑替代方案或将此类依赖抽象出来以便维护。 --- ### 结合其他引用的理解补充说明 虽然上述解释主要围绕 `___attribute__` 开展讨论,但是为了更全面理解整个上下文中提到的技术概念,这里还简单提及几个相关内容作为对比学习材料: - 访问控制机制中的 `protected`: 它限定了类内部成员访问权限范围仅限本体加子代继承关系之内[^2]; - Python 的胶囊导入工具(PyCapsule_Import()): 主要服务于动态加载共享库接口场景下验证一致性需求[^3]; - TensorFlow 队列操作实例展示了张量队列取出元素过程[^4]. 以上均体现了不同编程语言各自特色化设计思路和技术实现细节差异之处. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值