嵌入式编程中的 __attribute__ 到底是什么

2023-05-16

嵌入式编程中的 attribute 到底是什么

相信阅读嵌入式代码的老铁经常看到一些类型定义、变量、函数有 attribute 标识符,这个标识符号到底是做什么的?有哪些用法,咱们今天就来聊一聊。

attribute 可以指定编译时的细节。其可作用于变量、函数、结构体、结构体成员。
值的注意的是,Attributes 机制并不是 C标准 的一部分。因此,使用 Attributes 的程序有时候不可跨编译器移植。但是,目前市面上多数的 C/C++ IDE使用的编译工具链都是 GCC,包括很多的商业 IDE 的编译工具链也是基于 GCC 优化过的版本。因此 GNU C的__attribute__ 一般是可以兼容的,在 uboot 和 Linux 源码中会常用到此__attribute__机制。

attribute 格式

__attribute__ ((attribute-list)) 

例子:

extern void  die(const char *format,...)    __attribute__( (noreturn) );  // 只含一个属性 noreturn

extern void  die(const char *format,...)    __attribute__( (noreturn,  format(printf, 1, 2)) ); // 含两个属性:noreturn,  format(printf, 1, 2);两个属性以逗号分隔

一些常见的 attribute 属性

attribute((deprecated))

示例:用于版本管理,声明该函数将被丢弃,在编译时会发出提示,提醒开发人员使用最新版本的函数或变量。

int spi_cal_clock(int fapb, int hz, int duty_cycle, uint32_t* reg_o) __attribute__((deprecated));

编译时会提示:attribute_deprecated.c:33: warning: ‘ spi_cal_clock()’ is deprecated。

attribute((always_inline)) 与 attribute ((noinline))

在 内联函数(inline 函数)详解 中我们介绍了内联函数与非内联函数的区别。attribute((always_inline)) 与 attribute ((noinline)) 均作用于编译器,在编译阶段试图强制使得对应的函数内联或者非内联。
示例:

__attribute__((always_inline)) inline int32_t add(int32_t x, int32_t y) {

​	return (x+y);

}

上面的函数有两个 inline。后面的 inline 可能被编译器忽略(比如 c89 不支持该关键字,C99 支持该关键字)。但前面的 attribute((always_inline)) (GCC 编译器支持的属性) 不会被忽略,否则会报错。

attribute((noreturn))

允许一个函数不执行到 return 语句。

static void __ubsan_default_handler(struct source_location *loc, const char *func)
{
    char msg[60] = {};
    (void) strlcat(msg, "Undefined behavior of type ", sizeof(msg));
    (void) strlcat(msg, func + strlen("__ubsan_handle_"), sizeof(msg));
    esp_system_abort(msg);
}

直接编译上述语句可能触发 warning: this function may return with or without a value。
应使用下述声明说明该函数最终不返回,因为直接触发了 abort 异常,设备会重启,因此不必返回。

static void __ubsan_default_handler(struct source_location *loc, const char *func) __attribute__((noreturn));

attribute((overloadable))

声明支持重载。用于c语言函数,可以定义若干个名称相同,但传递的参数不同的函数,调用时编译器会自动根据参数选择函数原型。

__attribute__((overloadable)) void print(NSString *string)

__attribute__((overloadable)) void print(Int *int)

attribute((alias()))

用于设置函数别名。

int __centon() 
{
  printf("in %s\n",__FUNCTION__);
  return 0;
}

void centon() __attribute__((alias("__centon")));//设置函数别名,原函数是__cencon, 别名是centon. 后面可以使用 centon 代替 __centon.

attribute((nonnull()));

用于声明一个函数的指定参数不能为 null

extern void *
my_memcpy (void *dest, const void *src, size_t len)
        __attribute__((nonnull (1, 2))); // 参数 dest、src 不能为 null,否则编译器会发出警告

attribute((constructor(PRIORITY))) 与 attribute((destructor(PRIORITY)))

构造属性(constructors)和析构属性(destructors)属性,可带优先级(PRIORITY)。可以作用于函数和全局变量对象(或静态变量),这里以函数为例。
带有"构造"属性的函数将在main()函数之前被执行,而声明为"析构"属性的函数则将在main()退出时执行。主要用于在 main() 函数执行前后执行很多的前处理动作或者是后处理动作。

void main_enter1() __attribute__((constructor(99)));//main_enter函数在进入main函数前调用
void main_enter2() __attribute__((constructor(100)));//main_enter函数在进入main函数前调用
void main_exit() __attribute__((destructor));//main_exit函数在main函数返回后调用

上述函数,在进入 main() 之前,先调用 main_enter1(),因为它的优先级高为 99,然后调用 main_enter2(),因为它的优先级次高 100。最后在 main() 执行后,将调用main_exit() 。

对齐属性 attribute((aligned(x))) 与__attribute__((packed))

对齐属性可以用于指定内存的对齐方式。对齐值必须是 2 的整数幂。
如,int8_t foo; 可以被存储在 0x100\0x101\0x102\0x103 任意位置。
但是通过 int8_t foo attribute((aligned(4))); 指定其对齐方式为 4 bytes。然后其对齐就变成 0x100\0x104\0x108 了。
示例,请计算下面四种类型的变量的长度:

typedef struct {
    int8_t var1;
    int32_t var2;
    int8_t var3;
} custom_type1;

typedef struct {
    int8_t var1 __attribute__((aligned(4)));
    int8_t var2 __attribute__((aligned(4)));
    int8_t var3 __attribute__((aligned(4)));
}custom_type2;

typedef struct {
    int8_t var1;
    int32_t var2;
    int8_t var3;
}custom_type3 __attribute__((aligned)); // 自动按字对齐,如果aligned 后面不紧跟一个指定的数字值,那么编译器将依据你的目标机器情况使用最大最有益的对齐方式。ligned 属性使被设置的对象占用更多的空间,相反的,使用packed 可以减小对象占用的空间。需要注意的是,attribute 属性的效力与你的连接器也有关,如果你的连接器最大只支持16 字节对齐,那么你此时定义32 字节对齐也是无济于事的。ligned 属性使被设置的对象占用更多的空间,相反的,使用packed 可以减小对象占用的空间。

typedef struct {
    int8_t var1;
    int32_t var2;
    int8_t var3;
} custom_type4 __attribute__((packed)); // 不自动对齐。此时 sizeof( custom_type4) 应该是 6bytes. 使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐。使用该属性对struct或者union类型进行定义,设定其类型的每一个变量的内存约束。当用在enum类型定义时,暗示了应该使用最小完整的类型。告诉(不是强制)编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。

探究不同对齐方式下,最终结构体的大小。答案在文末。

attribute ((section (xx)))

section控制变量或函数在编译时的段名。在嵌入式软件开发时用的非常多,比如有外扩 Flash 或 RAM 时,需要将变量或函数放置到外扩存储空间,可以在链接脚本中指定段名来操作。在使用MPU(存储保护)的MCU编程时,需要对存储器划分区域,将变量或代码放置到对应的区域,通常也是通过段操作来实现。

提到section,就得说代码存放的基本区域了。在编译器编译之后,代码被划分为不同的段,RO Section(ReadOnly)中存放代码段和常量,RW Section(ReadWrite)中存放可读写静态变量和全局变量,ZI Section(ZeroInit)是存放在RW段中初始化为0的变量。
attribute((section(“section_name”))),其作用是将作用的函数或数据放入指定名为"section_name"对应的段中。

const int identifier[3] __attribute__ ((section ("ident"))) = { 1,2,3 };

void myfunction (void) __attribute__ ((section ("ext_function")))

上述代码分别在编译后,数组和函数所在的段分别为“indent”和“ext_function”而不是通常的 text 段中。

attribute((unused)) 与 attribute((used))

在C程序中,如果定义了一个静态函数\变量a,而没有去使用,编译时会有一个告警:‘a’ defined but not used [-Wunused-function]。

uint16_t acl_length __attribute__((unused));

__attribute__((unused)) static uint32_t get_cause(void)
{
    uint32_t wakeup_cause = REG_GET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, \
                                            RTC_CNTL_WAKEUP_CAUSE);
    return wakeup_cause;
}

意味着函数或变量很可能未被使用,此时编译器根据__attribute__((unused))不会针对这个函数产生警告。
也可以将其声明在函数实现中没有使用过的参数上,例如:

int main(int argc __attribute__((unused)), char **argv)

attribute((used)) 的作用是告诉编译器,我声明的这个符号是需要保留的。被used修饰以后,意味着即使函数没有被引用,在编译链接时下也不会被优化。如果不加这个修饰,那么链接器可能会去掉没有被引用的段。

attribute((weak))

若两个或两个以上全局符号名字一样,而其中之一声明为weak symbol(弱符号),另一个没有添加 weak 修饰符,则这些同名的全局符号不会引发重定义错误。当没有添加 weak 修饰符的对象存在时,链接器会忽略掉弱符号修饰的那个,如果不存在添加 weak 修饰符的对象,则使用 weak 修饰的那个对象。
感兴趣的可以参考我前述的博客:详解 C 语言中的弱符号与弱引用。

attribute((const))

该属性只能用于带有数值类型参数的函数上。当重复调用带有数值参数的函数时,由于返回值是相同的,所以此时编译器可以进行优化处理,除第一次需要运算外, 其它只需要返回第一次的结果就可以了,进而可以提高效率。该属性主要适用于没有静态状态(static state)和副作用的一些函数,并且返回值仅仅依赖输入的参数。

__attribute__((const)) int f() {
    return 1;
}

更多的__attribute__属性可以参考GCC手册,在我们需要使用到编译器一些高级特性的时候,可以在手册中查找。

总结

  1. __attribute__是一个编译属性,用于向编译器描述特殊的编译标识,可以用于编译程序时的错误检查或高级优化。它是GNU C特色之一,系统中有许多地方使用到。
  2. 高级嵌入式开发工程师应该对编译的优化选项增加了解,比如 attribute 机制,可以实现更高级的用法,写出更安全可靠的好程序。

对齐属性的答案

custom_type1 len = 12 // 编译器按照默认自动对齐
custom_type2 len = 12 // 每个成员都 4bytes 对齐
custom_type3 len = 12 // 32 位编译器默认 4bytes 对齐
custom_type4 len = 12 // 因为我的编译器禁止这种 packed 优化,因此最终会在编译时提出警告,但仍以最优长度进行对齐

(感谢收藏与点赞,您的支持是我持续创作的动力)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式编程中的 __attribute__ 到底是什么 的相关文章

  • 【CCF推荐专区】计算机类优质SCI&EI好刊,期刊质量高,部分期刊仅有少量版面

    x1f308 智能传感类 xff08 TOP xff09 CCF C类 期刊简介 IF 7 0 8 0 xff0c JCR1区 xff0c 中科院2 1区 检索情况 SCI amp EI 双检 xff0c 正刊 xff0c CCF C类 征
  • 考研杭电非全计算机技术调剂上岸

    考研杭电非全计算机技术调剂上岸 简介初试考完心态复试写这个的目的 简介 昨天看见杭电考研群里说到传承 xff0c 我觉得很有价值 xff0c 所以打算也写了我的一些想法 xff0c 希望对你们有帮助 本人31岁 xff0c 有老婆有孩子 x
  • 各种滤波算法的比较

    原文地址 xff1a http www wtoutiao com p 1fe9dPI html xfeff 各种滤波算法的比较 数字滤波 digital filtering 数字滤波 digital filtering 用数字设备 xff0
  • 十九.Linux开发之根文件系统移植——根文件系统的原理

    有道云笔记地址 xff1a 详情看这里链接 xff0c 记录太多 xff0c 就不一一排版了 http note youdao com noteshare id 61 f9c7c1b589233d7b6ed661c3749f1ce8 amp
  • (转)临界区,互斥量,信号量,事件的区别(线程同步)

    四种进程或线程同步互斥的控制方法 1 临界区 通过对多线程的串行化来访问公共资源或一段代码 xff0c 速度快 xff0c 适合控制数据访问 2 互斥量 为协调共同对一个共享资源的单独访问而设计的 3 信号量 为控制一个具有有限数量用户资源
  • Git clone的使用方法

    使用Git clone项目 1 首先我们要确保我们的电脑上已经安装Git 桌面点击右键出现如下图所示的两个Git即Git已经安装 2 在电脑的任意一个磁盘里新建一个本地文件夹作为clone项目的保存文件夹 3 在码云上面打开我们要clone
  • error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“0”不匹配值“2”

    报错1 xff1a error LNK2038 检测到 ITERATOR DEBUG LEVEL 的不匹配项 值 0 不匹配值 2 解决 xff1a xff08 1 xff09 工程的模式和库的模式不一致 xff0c 工程为Debug模式
  • 关于p操作和v操作的理解

    操作系统之PV操作 今天在做操作系统老师布置的操作系统作业 xff0c 但是碰到了一个有关pv操作的问题 xff0c 由于对pv操作的理解不是很透彻 xff0c 所以我查阅了很多资料 xff0c 下面来简单的通俗的介绍一下pv操作 1 信号
  • 相机针孔模型----从世界坐标系,到相机坐标系,再到图像物理坐标系,最后到图像像素坐标系的转换过程解析

    看了很多讲解针孔相机模型中从世界坐标系 gt 到相机坐标系 gt 图像坐标系的文章 xff0c 心里的疑惑也逐渐展开 xff0c 现在总结一下自己的理解 xff1a 世界坐标系 相机坐标系 图像物理坐标系 图像像素坐标系在我的另一篇博文里已
  • 如何在cmd查看文件内容的MD5值

    在cmd下进入 要查看的文件目录 进入cmd xff0c 输入如下命令 xff1a certutil hashfile 文件名称 文件类型 MD5 结果显示如下 SHA1 的 test py 哈希 364ebe3569456ec7e77de
  • CMakeLists.txt文件

    举例说明 xff1a CMake 构建 HelloSlam 工程 1 Ctrl 43 Alt 43 T 按键打开终端 2 在选定路径下建立工程文件夹 xff1a mkdir HelloSlam 3 构建 HelloSlam 的文件目录结构
  • PX4 CMakeLists.txt分析

    简单的概述 make 和 cmake 是linux UNIX系统下广泛使用的构建编译规则工具 xff0c 面对复杂庞大的工程 xff0c 各种源文件和工具文件分布在工程目录下 xff0c 如何组织和有序地编译和使用这些文件 xff0c 显然
  • iOS OC消除黄色警告⚠️ (不断的更新中...)

    开发一个项目时 xff0c 难免会产生很多警告 xff0c 一些是第三方或是老代码不再被支持造成的 xff0c 但并不影响使用 xff0c 这些警告其实可以直接隐藏掉 xff01 还有一些 警告可能是系统方法弃用 不兼容指针类型 未使用变量
  • AUTOSAR E2E & SecOC Comparison

    AUTOSAR E2E amp SecOC Comparison 前面已经介绍过了E2E 和 SecOC CMAC 了 xff0c 既然2者都可以进行数据完整性保护 xff0c 那么2者有什么区别呢 下面基于我的经验所总结 欢迎补充 xff
  • 解决:source devel/setup.bash(只在当前终端生效)的问题,使其在其他终端

    解决方法 xff1a 终端输入 xff1a gedit bashrc 文件打开后直接翻到最后面 在底部添加source catkin ws devel setup bash 保存退出即可
  • ROS与C++入门教程

    https www ncnynl com archives 201701 1279 html
  • HiChart图表统计:jsp中hichart用法以及从后台获取数据

    在做web服务器时 xff0c 用到了图表 xff0c 对一天内资源的下载量进行统计 xff0c 让数据更加的直观 上网查了很多资料 xff0c 最后发现HiChart很好用 xff0c 相对比较简单 下面作以详细介绍 xff1a 1 首先
  • [论文]欠驱动水下机器人的平面轨迹规划与跟踪控制设计

    论文 欠驱动水下机器人的平面轨迹规划与跟踪控制设计 摘要 研究了欠驱动自主水下航行器在水平面上的轨迹规划与跟踪控制的组合问题 给定光滑的 惯性的二维参考轨迹 xff0c 规划算法利用车辆动力学计算参考方向和机体固定速度 利用这些 xff0c
  • sql查询语句汇总,先撸为敬

    一 简单查询语句 group by 和having的区别 链接 二 复杂查询 1 数据分组 max min avg sum count SQL gt SELECT MAX sal MIN age AVG sal SUM sal from e
  • 最优化的基本概念

    最优化的基本概念 连续和离散优化问题无约束和约束优化问题随机和确定性优化问题线性和非线性规划问题凸和非凸优化问题全局和局部最优解优化算法 一般来说 xff0c 最优化算法研究可以分为 xff1a 构造最优化模型 确定最优化问题的类型和设计算

随机推荐

  • [RISCV]为RISC-V移植FreeRTOS系列之一 -- 目录结构

    前言 写这篇文章的时候 xff0c 我基本已经完成了这项工作了 xff0c 花了一周的时间来把freertos porting到Andes公司的N25 riscv core上 xff0c 本来其实是想支持国产的RT Thread xff0c
  • [RISCV]为RISC-V移植FreeRTOS系列之三 -- 时基

    前言 书接上回 xff0c 上回说到我们已经做好了准备 xff0c 所谓万事具备 xff0c 就差一场东风 xff0c 而能吹动FreeRTOS这条大船的是什么呢 xff1f 没错 xff0c 聪明的你已经猜到了 xff0c 是时基 有过其
  • [RISCV]为RISC-V移植FreeRTOS系列之四 -- 中断与trap handler

    前言 上回说到了我们已经把系统的心跳动起来了 xff0c 但是这里面还有一个问题 xff0c 我们都知道timer中断 xff0c 中断的trap怎么来的呢 这回就来解决这个事情 作者 xff1a wangyijieonline 链接 xf
  • [RTOS]uCOS、FreeRTOS、RTThread、RTX等RTOS的对比之特点

    最近正好又重新回顾了一下这几款OS xff0c 心里一直有个疑问 xff0c 明明这几款RTOS是这么像 xff0c 为什么还要搞出这么多个来呢 xff0c 最后的结论就是 xff0c 管他呢 xff0c 反正哪个用的顺手用哪个 本篇博客就
  • git submodule

    此文已由作者张磊薪授权网易云社区发布 欢迎访问网易云社区 xff0c 了解更多网易技术产品运营经验 前言 submodule 目前对 git 仓库拆分的已有实现之一 环境 git version 2 7 4 windows 1 准备工作 首
  • FreeRTOS 通信方式

    文章目录 一 消息队列二 信号量三 互斥量四 事件五 通知 一 消息队列 消息队列是一种常用于任务间通信的数据结构 xff0c 队列可以在任务与任务间 中断和任务间传递信息 读写队列均支持超时机制 1 创建队列 QueueHandle t
  • 芯片、模组、开发板的区别与联系-结合ESP32浅谈

    1 从外形说起 xff1a 1 1芯片 没错 xff0c 这块黑色的小硅片就是 芯片 本体 xff08 通常比大拇指还小 xff0c 内部集成了实现特定功能的硬件集成电路 xff09 1 2模组 由上述芯片研发的模组是这样的 xff1a 从
  • 一文读懂局域网、广域网、WLAN、WiFi的联系与区别

    1 引言 最近总有小伙伴问我 xff0c 广域网 局域网的区别与联系 WLAN与WiFi的关系 xff0c 遂写此文 xff0c 以作解答 2 广域网与局域网 广域网 xff08 Wide Area Network xff09 xff0c
  • RTOS 和裸机系统的异同-基于 ESP32 学习双核 FreeRTOS 的使用

    Learning FreeRTOS with esp32 什么是 RTOS 其本质上是运行在小型嵌入式设备上的特殊软件 系统软件 如同手机的安卓系统软件 windows 系统软件 RTOS VS 裸机系统 传统的裸机系统 xff08 无操作
  • u盘打开之后就只有一个快捷方式

    我今天也出现了这种问题 xff0c 百度一下发 现都解决不了 xff0c 然后自己尝试了一个新的方法 xff1a 其实还有一个又简单又好用又快捷的方法就是 1 只要你记得你的U盘里的任何一个文件或者文件夹的名称 xff0c 2 然后搜索U盘
  • FreeRTOS 删除任务

    FreeRTOS 删除任务 概述 任务的删除使用的 API 为 xff1a void vTaskDelete TaskHandle t xTask 任务删除主要是两种情况 xff1a 自删除 xff0c 即在任务本身的 TaskCode 中
  • 使用 stream buffer 传递数据

    使用 stream buffer 传递数据 概述 如前所述 xff0c 队列虽然提供了任务之间传递数据的功能 xff0c 但没有对通知机制进行优化 xff0c 即不方便实现多次采集不同长度的数据 xff0c 然后触发一次通知接收的机制 特性
  • 使用 message buffer 传递数据

    使用 message buffer 传递数据 概述 MessageBuffer xff0c 即消息缓冲区 xff0c 是在流式缓冲区的基础上实现的针对离散消息的专用通信组件 xff0c 其进一步针对 消息 进行设计改进 在 StreamBu
  • FreeRTOS 任务间通信与同步总结

    FreeRTOS 任务任务同步与数据传递 xff08 通信 xff09 总结 概述 本章主要介绍了 RTOS 系统中数据传递的机制 根据数据传递的目的 xff0c 可以分为同步 消息通信两种 其中同步是指协调程序运行的先后顺序 xff0c
  • RTOS 中 Task 之间资源共享示例

    RTOS 中 Task 之间资源共享示例 什么是共享资源 大型项目往往需要创建多个任务 xff0c 任务之间协同合作完成一个大型的功能 在前述的章节中 xff0c 我们讲述了任务间的同步与通信 xff0c 但合作与竞争总是相辅相成的 任务
  • RTOS共享资源保护-优先级反转与解决策略

    RTOS 中的优先级反转与解决策略 概述 上节讲述了可以使用二值信号量实现任务 任务之间的共享资源的保护 二值信号量的确完成了保护共享资源的任务 但在一些情况下 这种策略会带来副作用 即优先级反转 优先级反转是如何产生的 理想情况下 按照我
  • RTOS 驱动开发篇-通过 RTOS 组件实现按键驱动-优化1

    RTOS 驱动开发篇 通过 RTOS 组件实现按键驱动 优化1 概述 一个好的驱动程序需要数据关系清晰 代码可复用性高 并且便于维护 如在 RTOS 驱动开发篇 通过 RTOS 组件实现按键驱动1 中所述的那样 当前的按键驱动代码只是为了让
  • RTOS 驱动开发篇-通过 RTOS 组件实现按键驱动-优化2

    RTOS 驱动开发篇 通过 RTOS 组件实现按键驱动 优化2 概述 一个好的驱动程序需要数据关系清晰 代码可复用性高 并且便于维护 如在 RTOS 驱动开发篇 通过 RTOS 组件实现按键驱动1 中所述的那样 基础版本的按键驱动代码只是为
  • 物联网应用选择 RTOS 还是 Linux?

    物联网应用选择 RTOS 还是 Linux Linux VS RTOS xff0c 我该选哪个 xff1f 引言 在开发设备或系统时 xff0c 您需要做出的最早和最关键的决定之一就是决定它将运行哪种类型的操作系统 操作系统是基于特定硬件的
  • 嵌入式编程中的 __attribute__ 到底是什么

    嵌入式编程中的 attribute 到底是什么 相信阅读嵌入式代码的老铁经常看到一些类型定义 变量 函数有 attribute 标识符 xff0c 这个标识符号到底是做什么的 xff1f 有哪些用法 xff0c 咱们今天就来聊一聊 attr