linux gcc __attribute__

news2024/11/22 19:36:19

__attribute__

  • 1. 函数属性
    • 1.1 __attribute__((noreturn))
    • 1.2 __attribute__((format))
    • 1.3 __attribute__((const))
  • 2. 变量属性
    • 2.1. __attribute__((aligned))
    • 2.2. __attribute__((packed))
  • 3. 类型属性

__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)):该属性用于指示编译器该类型可能与其他类型进行别名访问,即可能会将不同类型的指针视为相同类型的别名指针。这在某些特定的编程场景中可能会有用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/841316.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

测试该知道的二三事:浅谈响应式网页设计

1.起因 最近几天正巧在帮朋友的公司团队做质量保障体系的培训&#xff0c;在此期间与几个测试人员闲聊&#xff0c;正是其中的一件事让我对今天的话题提起了兴趣&#xff1a;朋友公司里的研发团队招了一个应届毕业生&#xff0c;做了半年之后接了某个web项目的其中一个拓展功能…

质检工具(FindBugs、CheckStyle、Junit、Jmeter、Apifox)

1、Findbugs IDEA软件中可以装该插件,2018版本以前主要搜索FindBugs-IDEA 、2018版本以后主要搜索 SpotBugs。 1.1、FindBugs-IDEA安装及使用流程: 1.2、SpotBugs安装及使用流程: 2、Checkstyle IDEA软件中可以装该插件,所有版本的插件一致:CheckStyle 2.1、安装流程…

【C# 基础精讲】为什么选择C# ?

C#&#xff08;C Sharp&#xff09;是由微软开发的一种通用、面向对象的编程语言。它最初于2000年发布&#xff0c;自那时以来逐渐成为开发者的首选之一。C#的设计目标是提供一种简单、现代、可靠且安全的编程语言&#xff0c;使开发者能够轻松构建各种类型的应用程序。 为什么…

Apache Doris 助力中国联通万亿日志数据分析提速 10 倍

本文导读&#xff1a; 在数据安全管理体系的背后&#xff0c;离不开对安全日志数据的存储与分析。以终端设备为例&#xff0c;中国联通每天会产生百亿级别的日志数据&#xff0c;对于保障网络安全、提高系统稳定性和可靠性具有至关重要的作用。目前&#xff0c;Apache Doris 在…

解决树莓派“由于没有公钥,无法验证下列签名“

目录 简介&#xff1a;在换完国内源后&#xff0c;树莓派尝试更新同步/etc/apt/sources.list和/etc/apt/sources.list.d中列出的软件源的软件包版本也就是&#xff08;apt-get update&#xff09;和更新已安装的所有或者指定软件包&#xff08;也即是apt-get upgrade&#xff0…

java+springboot摄影作品竞赛报名系统 微信小程序--论文

随着Internet的发展&#xff0c;人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化&#xff0c;网络化和电子化。网上管理&#xff0c;它将是直接管理摄影竞赛小程序的最新形式。本小程序是以构建摄影竞赛为目标&#xff0c;使用java技术制作&#xff0c;…

xLua学习

xLua教程&#xff1a;https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/XLua%E6%95%99%E7%A8%8B.md xLua配置&#xff1a;https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/configure.md FAQ&#xff1a;https://github.com/Tencent/xLua/blob/maste…

【Leetcode】反转链表||基础题||击败100%(头插法)

step by step. 题目&#xff1a; 反转链表&#xff0c;基础题了&#xff0c;要很熟练的境界—— 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示…

awk筛选给定时间范围内的日志

目录标题 筛选日志时间示例编写1.awk文件准备一个需要筛选时间的日志执行awk命令 筛选日志时间 grep/sed/awk用正则去筛选日志时&#xff0c;如果要精确到小时、分钟、秒&#xff0c;则非常难以实现。 但是awk提供了mktime()函数&#xff0c;它可以将时间转换成epoch时间值。 …

opencv基础43- 图像梯度-Laplacian 算子( cv2.Laplacian)边缘检测基础

Laplacian算子是一种图像处理中常用的边缘检测算子&#xff0c;它用于检测图像中的边缘和轮廓。该算子计算图像中每个像素点的二阶导数&#xff0c;从而突出图像中灰度值变化较大的区域&#xff0c;这些区域通常对应图像的边缘或者轮廓。 Laplacian&#xff08;拉普拉斯&#x…

【Spring Cloud 七】Sleuth+Zipkin 链路追踪

Sleuth链路追踪 系列博客背景一、 什么是链路追踪二、为什么要有链路追踪三、Sleuth与ZipkinSleuthZipkinSleuth和Zipkin的关系是什么&#xff1f; 四、使用Sleuthzipkin进行链路追踪4.1下载zipkin4.2案例说明项目代码服务提供者pom文件yml配置文件项目启动类controller 抽离出…

Android network — iptables四表五链

Android network — iptables四表五链 1. iptables简介2. iptables的四表五链2.1 iptables流程图2.2 四表2.3 五链2.4 iptables的常见情况 3. NAT工作原理3.1 BNAT3.2 NAPT 4. iptables配置 本文主要介绍了iptables的基本工作原理和四表五链等基本概念以及NAT的工作原理。 1. i…

MyBatis-Flex 一个优雅的 MyBatis 增强框架

文章目录 网站MyBatis-Flex 是什么特征1、很轻量2、只增强3、高性能4、更灵动 快速开始hello world&#xff08;原生&#xff09;QueryWrapper 示例select *select columnsselect functionswherewhere 动态条件 1where 动态条件 2where 自动忽略 null 值where selectexists, no…

AlmaLinux 9 安装 Go 1.20

AlmaLinux 9 安装 Golang 1.20 1. 下载 go 安装包2. 安装 go3. 配置环境变量4. 确认 go 版本 1. 下载 go 安装包 访问 https://go.dev/dl/&#xff0c;下载你想安装的版本&#xff0c;比如 go1.20.7.linux-amd64.tar.gz&#xff0c; 2. 安装 go (可选)删除旧版本&#xff0c;…

检查网站是HTTP那种协议与获取域名的ipv6地址

前言 最近在做HTTPS的应用&#xff0c;可能需要使用ipv6的地址做SLB&#xff0c;但是怎么检查配置正确&#xff0c;总不能每次都看日志吧&#xff0c;实际上客户端也很容易查看&#xff0c;总结工作经验。 检查HTTP协议版本 笔者想到了使用浏览器方式&#xff0c;或者抓包&a…

div 按钮和 button 按钮

我们通常可以通过 HTML 元素结合需求的样式作为按钮触发一些事件&#xff0c;但原始 button 标签其实内置了许多功能&#xff0c;并且可以通过一些方法&#xff0c;一键清楚默认样式&#xff0c;从而定制需求样式。 首先是原始的 button 标签和 div 标签 作为按钮时的效果展示…

【N32L40X】学习笔记14-在RT-thread系统中读取eeprom数据

eeprom 说明 eeprom介绍 AT24C01A&#xff0c;1K串行EEPROM&#xff1a;内部组织16页8字节&#xff0c;1K需要一个7位数据字地址进行随机字寻址。AT24C02,2K串行EEPROM&#xff1a;内部组织32页8字节&#xff0c;2K需要一个8位数据字地址进行随机字寻址。AT24C04,4K串行EEPRO…

JDK动态代理的原理解析、代码实现

代理就像是&#xff1a;买家(客户端)——销售(代理对象)——工厂(目标) 买家不用直接去工厂买&#xff0c;而是直接通过销售就可以购买到&#xff0c;假设工厂生产的是杯子&#xff0c;那么工厂只需要提供杯子&#xff0c;而销售在不改变杯子的生产过程的情况下对杯子进行包装设…

C语言单链表OJ题(较难)

一、链表分割 牛客网链接 题目描述&#xff1a; 现有一链表的头指针 ListNode* pHead&#xff0c;给一定值x&#xff0c;编写一段代码将所有小于x的结点排在其余结点之前&#xff0c;且不能改变原来的数据顺序&#xff0c;返回重新排列后的链表的头指针。 思路&#xff1a;…

idea如何开启远程调试

一&#xff1a;打包需要部署的jar包上传到服务器 二&#xff1a;服务器&#xff08;开启远程调试接口&#xff09; nohup java -jar -Xdebug -Xrunjdwp:transportdt_socket,servery,suspendn,address8453 xxx.jar > xxx.log 2>&1 & 三&#xff1a; idea配置rem…