linux gcc __attribute__

2023-10-30

__attribute__ 是 GCC 编译器提供的一种特殊语法,它可以用于设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。

1. 函数属性

1.1 attribute((noreturn))

__attribute__((noreturn)) 通常应用于那些“不会返回”的函数,即程序就直接在这个函数内退出了。例如:

  1. 程序异常处理

在程序遇到错误或异常时,可能需要立即终止程序并输出错误信息。这种情况下,可以使用 __attribute__((noreturn)) 注解来标记一个名为 die 的函数,以确保函数执行完之后程序不会继续运行。

void die(const char* msg) __attribute__((noreturn));

void die(const char* msg) {
    fprintf(stderr, "Error: %s\n", msg);
    exit(1);
}

在上面的代码中,我们定义了一个名为 die 的函数,并使用 __attribute__((noreturn)) 注解表示该函数不会返回。在函数内部,我们使用 fprintf 打印错误信息,并调用 exit(1) 函数来终止程序。由于 die 函数被标记为 noreturn,因此编译器保证在执行完 die 函数之后程序不会继续运行。

  1. 核心转储文件生成

在 Linux 系统中,当程序异常终止时,可以通过设置 coredump 文件来捕获程序状态和内存快照等信息,以便于进行故障排查和调试。在生成核心转储文件时,可以使用 __attribute__((noreturn)) 注解来标记一个名为 abort 的函数,以确保函数执行完之后程序不会继续运行。

void abort() __attribute__((noreturn));

void abort() {
    signal(SIGABRT, SIG_DFL);
    raise(SIGABRT);
    exit(EXIT_FAILURE);
}

在上面的代码中,我们定义了一个名为 abort 的函数,并使用 __attribute__((noreturn)) 注解表示该函数不会返回。在函数内部,我们使用 signal 设置信号处理函数为默认值,并调用 raise 来生成 SIGABRT 信号,然后再调用 exit 函数来终止程序。由于 abort 函数被标记为 noreturn,因此编译器保证在执行完 abort 函数之后程序不会继续运行。

总的来说,__attribute__((noreturn)) 通常适用于那些具有明确终止程序任务的函数,例如异常处理、断言失败、致命错误等。它可以帮助开发者更好地控制程序流程和性能,并减少潜在的漏洞和错误。

1.2 attribute((format))

__attribute__((format))用于指定函数参数的格式化字符串。它可以帮助编译器在编译时对函数调用进行检查,以确保传递给被调用函数的参数与函数定义中的格式化字符串相匹配。
使用__attribute__((format))可以帮助编译器进行静态类型检查,以便捕获在编译时可能导致格式化字符串错误的问题。例如,如果格式化字符串中指定了一个整数参数,但实际传递给函数的是一个字符串参数,编译器将发出警告或错误消息。

以下是一个使用__attribute__((format))的示例:

#include <stdio.h>

void my_printf(const char *format, ...) __attribute__((format(printf, 1, 2)));

/*
 * 模拟标准库函数printf,它接受一个格式化字符串和可变数量的参数,并将格式化后的输出打印到标准输出。
 * 通过使用va_list和相关的宏来处理可变参数,函数能够将格式化字符串和参数传递给vprintf函数进行输出。
 * 需要注意的是,这个函数没有进行格式字符串和参数的类型检查,所以在使用时需要确保格式化字符串与参数的类型和数量相匹配,以避免潜在的运行时错误。
 */
void my_printf(const char *format, ...)
{
    // va_list是一个类型,用于存储可变数量的参数。
    va_list args;
    // va_start宏用于初始化args,使其指向可变参数列表的起始位置。第二个参数是最后一个非可变参数,即格式化字符串format。
    va_start(args, format);
    // vprintf是一个标准C函数,用于根据格式化字符串format和可变参数列表args打印输出。它与printf函数类似,但接受一个va_list参数而不是可变数量的参数。
    vprintf(format, args);
    // va_end宏用于结束对可变参数的访问,清理相关资源。
    va_end(args);
}

int main()
{
    int num = 10;
    my_printf("The number is: %d\n", num);  // 正确的使用
    my_printf("The number is: %s\n", num);  // 错误的使用,编译器可能会发出警告或错误消息
    return 0;
}

在上面的示例中,my_printf函数使用__attribute__((format(printf, 1, 2)))来指定它的第一个参数是格式化字符串,并且函数至少接受两个参数。这样,编译器在编译时将会检查函数调用的参数是否与格式化字符串相匹配。

应用场景
__attribute__((format))的主要应用场景是在编写格式化输出函数(如printffprintf等)或格式化输入函数(如scanffscanf等)时,用于增加编译时的类型检查和错误检测。

以下是一些实际应用场景:

  1. 自定义格式化输出函数:当你需要编写自定义的格式化输出函数时,可以使用__attribute__((format))来确保传递给函数的参数与格式化字符串匹配。这有助于避免在运行时出现格式化字符串错误导致的问题,提高代码的健壮性和可靠性。

  2. 日志记录:当你编写日志记录函数时,可以使用__attribute__((format))来确保日志消息的格式化字符串与实际参数匹配。这样可以在编译时捕获潜在的日志格式错误,避免在运行时出现错误的日志输出。

  3. 静态代码分析工具:静态代码分析工具可以利用__attribute__((format))来检测代码中的潜在问题。通过分析函数调用和格式化字符串,这些工具可以检查是否存在格式化字符串与参数不匹配的情况,并生成警告或错误报告。

总之,__attribute__((format))的主要应用场景是在需要进行格式化字符串的输出或输入操作时,提供编译时的类型检查和错误检测,以提高代码的安全性和可维护性。

需要注意的是,__attribute__((format))是一个编译器特定的扩展,不是标准C或C++的一部分,因此在不同的编译器中可能会有差异。

1.3 attribute((const))

__attribute__((const))用于标记函数为纯函数(Pure Function)。纯函数是指函数的返回值仅依赖于输入参数,而不受其他因素的影响,也不会对程序的状态产生任何可观察的副作用。

以下是关于__attribute__((const))的一些要点:

  1. 纯函数属性:通过将函数标记为__attribute__((const)),指示编译器该函数是一个纯函数。这意味着函数的返回值仅由函数的参数决定,并且在同样的输入下,函数的返回值始终相同。

  2. 优化机会:将函数标记为纯函数有助于编译器进行优化。编译器可以假设对于相同的输入参数调用纯函数的两个位置,其返回值也必定相同。这种假设使得编译器可以进行常量传播(constant propagation)、公共子表达式消除(common subexpression elimination)等优化操作。

  3. 副作用限制:纯函数不应该对程序状态产生可观察的副作用。它们不应修改全局变量、静态变量,也不应进行I/O操作或内存分配等可能改变程序状态的操作。

  4. 示例用法:以下是一个示例函数,展示如何使用__attribute__((const))标记函数为纯函数:

int square(int x) __attribute__((const));

int square(int x) {
    return x * x;
}

在上述示例中,square函数被标记为纯函数。它只依赖于输入参数x的值,并返回x的平方。由于square函数没有副作用,编译器可以进行进一步优化,例如在常量传播时使用已知的输入值。

总结:__attribute__((const))函数属性用于标记函数为纯函数,以便编译器进行优化。这可以提供更好的代码优化和可读性,并确保函数没有对程序状态产生可观察的副作用。

2. 变量属性

2.1. attribute((aligned))

__attribute__((aligned(n))是GCC和Clang编译器提供的一个属性,用于指定变量、结构体、联合体和数组的对齐方式为n字节对齐。下面是对该属性的详细解释以及几个完整示例:

__attribute__((aligned(n))属性的解释:

  • __attribute__((aligned(n))是GCC和Clang编译器的扩展语法,用于控制内存对齐方式。
  • 它可以应用于变量、结构体、联合体和数组等数据类型的声明。
  • 通过将其放置在声明之后,可以指定所需的对齐方式,其中n表示对齐的字节数。

示例:


#include <stdio.h>

struct Point
{
    char arr[25] __attribute__((aligned(__BIGGEST_ALIGNMENT__))); // 以当前机器支持的最大字节对齐数__BIGGEST_ALIGNMENT__来对齐char数组
    double x;
};

struct Point2
{
    char arr[17] __attribute__((aligned(8))); // 以8字节对齐来对齐char数组
    double x;
};

struct Point3
{
    char arr[25]; 
    double x;
}__attribute__((aligned(__BIGGEST_ALIGNMENT__))); // 以当前机器支持的最大字节对齐数__BIGGEST_ALIGNMENT__来对齐结构体

int main()
{
    struct Point p; // 以16字节对齐的结构体变量

    printf("Size of Point: %zu\n", sizeof(struct Point));
    printf("Size of Point2: %zu\n", sizeof(struct Point2));
    printf("Size of Point3: %zu\n", sizeof(struct Point3));
    return 0;
}

运行结果:
在这里插入图片描述

attribute((aligned(n)))和#pragma pack(n)的区别
__attribute__((aligned(n)))#pragma pack(n) 都是用于控制内存对齐方式的机制,但它们之间存在一些区别:

  1. 语法:

    • __attribute__((aligned(n))) 是一种在 C 和 C++ 中使用的属性语法,直接应用于变量或结构体的声明。
    • #pragma pack(n) 是一个预处理器指令,需要放置在源代码中的适当位置。
  2. 作用范围:

    • __attribute__((aligned(n))) 可以直接应用于单个变量或结构体的声明,仅影响指定的实体。
    • #pragma pack(n) 影响其所在作用域内的所有变量或结构体的对齐方式,直到遇到另一个 #pragma pack 恢复默认对齐方式。
  3. 兼容性:

    • __attribute__((aligned(n))) 是 GNU C 和一些兼容的编译器支持的语法。它在符合标准的编译器中可能无效或产生不可预测的结果。
    • #pragma pack(n) 是一种更通用的指令,可以在多个编译器上使用,但具体行为可能会因编译器而异。
  4. 细粒度控制:

    • __attribute__((aligned(n))) 允许您对每个变量或结构体进行精确的对齐控制,可以指定不同的对齐方式。
    • #pragma pack(n) 设置了一个全局对齐方式,会对作用范围内的所有变量或结构体生效,并且无法针对特定实体进行个别设置。

2.2. attribute((packed))

attribute((packed)) 和 #pragma pack(1) 的作用是相同的,即指定结构体或变量的紧凑对齐方式。

无论是使用 attribute((packed)) 还是 #pragma pack(1),它们都会取消默认的对齐规则,使结构体或变量的成员按照紧凑方式存储在内存中,避免因对齐而导致的额外空间浪费或与其他系统的数据格式要求不匹配。


#include <stdio.h>

struct Point
{
    char arr[25];
    double x  __attribute__((packed));
};

int main()
{
    struct Point p; // 以16字节对齐的结构体变量

    printf("Size of Point: %zu\n", sizeof(struct Point));
    return 0;
}

运行结果:
在这里插入图片描述

3. 类型属性

通过使用 __attribute__,可以为函数、变量、结构体、枚举等类型添加特定的属性,以便在编译过程中进行控制或优化。

以下是一些常用的 __attribute__ 类型属性:

下面是对这几个 __attribute__ 类型属性的简要介绍:

  1. __attribute__((aligned(n))):这个属性用于指定变量或结构体的对齐方式。通过设置 __attribute__((aligned(n))),您可以将变量或结构体按照 n 字节对齐。例如,attribute((aligned(4)))` 将变量或结构体按照 4 字节对齐。

  2. __attribute__((packed)):该属性用于指定结构体或变量的紧凑对齐方式。默认情况下,编译器会根据对齐规则将结构体或变量的成员对齐到特定的边界上,以提高访问效率。使用 __attribute__((packed)) 属性可以取消对齐规则,使结构体或变量的成员以紧凑的方式存储。

  3. __attribute__((transparent_union)):透明联合是一种特殊类型的联合,允许将联合成员与外部成员具有相同的地址。使用 __attribute__((transparent_union)) 属性可以在联合类型中启用这种行为。

  4. __attribute__((unused)):该属性用于告诉编译器某个函数或变量可能未被使用。通过添加 __attribute__((unused)) 属性,可以避免编译器产生未使用变量或函数的警告。

  5. __attribute__((deprecated)):使用该属性可以将函数或变量标记为已过时。当使用被标记为过时的函数或变量时,编译器会发出警告,提醒开发者不再推荐使用。

  6. __attribute__((may_alias)):该属性用于指示编译器该类型可能与其他类型进行别名访问,即可能会将不同类型的指针视为相同类型的别名指针。这在某些特定的编程场景中可能会有用。

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

linux gcc __attribute__ 的相关文章

随机推荐

  • STM32速成笔记—IWDG

    文章目录 一 IWDG简介 二 STM32的IWDG 2 1 STM32的IWDG简介 2 2 喂狗 2 3 IWDG框图 三 IWDG配置步骤 四 IWDG配置程序 4 1 IWDG初始化程序 4 2 喂狗 五 应用实例 一 IWDG简介
  • 恶意代码的分类

    计算机病毒 注意这是狭义上的定义 和网络蠕虫都可以进行自我传播 那他们的本质区别是什么 计算机病毒 一组能进行自我传播 需要用户干预来触发执行的破坏性程序或代码 网络蠕虫 一组能进行自我传播 不需要用户干预即可触发执行的破坏性程序或代码 1
  • [007]爬虫系列

    一 找到首次加载的js 用源码面板打开 二 第一行打断点 格式化 三 刷新网页 成功断点 执行hook 代码如下 function use strict var cookieTemp Object defineProperty docume
  • 前端实现流程图效果_微前端在58商业的探索实践

    导读 商业FE部门分四个业务组 每个组内迭代着多个中后台应用 其中有跨组协作项目 这些项目的特点是UI UE相似 有较多的组件 逻辑复用场景 技术栈统一为VUE 且项目在不断迭代中 实践微前端架构能提高业务复用性 让各团队更高效的分治项目
  • npm,webpack学习中遇到的各种问题

    问题 打包的时候报错 Uncaught ReferenceError regeneratorRuntime is not defined 解决 详见babel plugin transform runtime babel pollyfill
  • Python字典操作方法--items()

    d items 返回字典中的所有键值对信息 返回结果是Python的一种内部数据类型dict items 如果希望更好的使用返回结果 可以将其转换为列表类型 键值对以元组类型 即括号形式 表示 gt gt gt d 2018 小明 2019
  • Flutter与Native通信原理剖析与实践

    通信原理 我们分几种场景来介绍Flutter和Native之间的通信 Native发送数据给Flutter Flutter发送数据给Native Flutter发送数据给Native 然后Native回传数据给Flutter Flutter
  • 系统服务器有哪几种,服务器的操作系统有哪几种

    服务器的操作系统有哪几种 内容精选 换一换 切换操作系统是为您的云服务器重新切换一个系统盘 切换完成后云服务器的系统盘ID会发生改变 并删除原有系统盘 如果云服务器当前使用的操作系统不能满足业务需求 如软件要求的操作系统版本较高 您可以选择
  • 【yarn】yarn RMContainer 状态机正常执行流程

    1 概述 上一篇文章 Yarn Yarn Service端如何处理客户端提交的任务 在上一篇文章中 我们知道服务器接收到客户端提交的任务之后 会启动多个状态机进行联合操作 最终来解决任务提交之后的全流程 多个状态机合作完成任务 然后我们看了
  • 工业相机SDK二次开发

    1 工业相机编程模型和流程 2 工业相机SDK接口使用总结 3 Basler Pylon工业相机SDK的使用 4 Pylon 以实时图像采集讲解PylonC SDK使用流程 5 关于使用维视工业相机 SDK 采集图像的问题 6 工业相机SD
  • 【快速上手系列】使用七牛云+webuploader实现对象云存储(OSS)

    快速上手系列 使用七牛云 webuploader实现对象云存储 OSS 步骤 一 七牛云配置 1 新建存储空间 进入七牛云 注册登录 进入控制台 选择对象存储 Kodo 选择空间管理 新建空间 免费30天 写好了之后点击确定 2 获取 Ac
  • es--基础--9.2--SpringBoot注解--Field--介绍

    es 基础 9 2 SpringBoot注解 Field 介绍 内容 Retention RetentionPolicy RUNTIME Target ElementType FIELD Documented Inherited publi
  • node笔记01——Nodejs学习之fs模块、最齐全的node.js学习,包含练习案例整理

    Nodejs学习之fs模块 初始node js 浏览的中的JavaScript主要是有JavaScript核心语法和webAPI组成 浏览器中因为使用了JavaScript解析引擎 所以JavaScript代码才可以再浏览器中被执行 不同浏
  • log4j2漏洞复现

    一 漏洞描述 apache log4j通过定义每一条日志信息的级别能够更加细致地控制日志生成地过程 受影响地版本中纯在JNDI注入漏洞 导致日志在记录用户输入地数据时 触发了注入漏洞 该漏洞可导致远程代码执行 且利用条件低 影响范围广 小到
  • python is和==的区别

    python 中 is和 的区别 在 Python 中会用到对象之间比较 可以用 也可以用 is 但是它们的区别是什么呢 is 比较的是两个实例对象是不是完全相同 它们是不是同一个对象 占用的内存地址是否相同 莱布尼茨说过 世界上没有两片完
  • Webpack中常见的Loader?解决了什么问题?

    一 是什么 loader 用于对模块的源代码进行转换 在 import 或 加载 模块时预处理文件 webpack做的事情 仅仅是分析出各种模块的依赖关系 然后形成资源列表 最终打包生成到指定的文件中 如下图所示 在webpack内部中 任
  • JetBrains IDE Support Chrome 安装的正确姿势

    文章目录 1 准备 2 步骤 2 1 解压 2 2 修改为 rar后缀名 2 3 再次解压 2 4 找到扩展程序 2 5 移动文件夹 2 6 安装效果 3 结束语 1 准备 Chrome浏览器 JetBrains IDE Support插件
  • DIY多快充协议太阳能充电器!----锂电池充电电路

    上一篇文章介绍了项目硬件框图中的三个主要部分 分别为BUCK降压拓扑为锂电池充电 BOOST升压拓扑将锂电池电压升至手机充电电压 LDO线性电源为单片机提供稳定3 3V电源 这篇文章主要来介绍BUCK降压拓扑和锂电池充电电路设计 在说明锂电
  • 【CSS】Less/sass(css预处理器)

    Less sass css预处理器 作用 1 嵌套2 变量3 传参4 计算 可以嵌套标签属性设置 如在less中写 css文件中可以生成 css中变量写为 格式较为繁琐 在less中可以写作 操作较为简单 而且less支持加减乘除的计算 这
  • linux gcc __attribute__

    attribute 1 函数属性 1 1 attribute noreturn 1 2 attribute format 1 3 attribute const 2 变量属性 2 1 attribute aligned 2 2 attrib