C语言之__attribute__

2023-05-16

公众号:嵌入式不难

__attribute__声明函数属性

可以使用__attribute__来声明函数的属性,这些属性可以帮助编译器优化调用或更仔细地检查代码的正确性。例如,属性(noreturn)用来指定函数从不返回。函数属性声明由__attribute__关键字引入,函数后跟一个用双括号括起来的属性说明。你可以在声明中指定多个属性,方法是在双括号里面用逗号将各个声明分开。

aligned

  • aligned(alignment)
    对齐属性指定函数的第一条指令的最小对齐方式,以字节为单位,对齐时必须是2的整数幂。

formart

  • format(archetype, string-index, first-to-check)
    format属性指定函数采用 printf、scanf、strftime 或 strfmon 样式参数,这些参数应根据格式字符串进行类型检查。
    –archetype指定了按照什么格式进行校验,参数应为printf、scanf、strftime 或 strfmon。
    –string-index指定了被声明函数的第几个参数(从1开始)为format格式化字符串参数。
    –first-to-check指定了被申明函数的第几个参数(从1开始)为可变参数的第一个参数。
    现有函数如下
extern int my_printf(void *my_object, const char *my_format, ...) __attribute__((format (printf, 2, 3)));
//摘取printf的定义为 int printf(const char *format, ...)
//archetype=printf:按照printf格式进行检查my_printf
//string-index=2:my_printf的第2个参数(my_format)对应了printf参数中的format
//first-to-check=3:my_printf的第3个参数(...)对应了printf参数中的...

此时编译器会检查 my_printf 的调用中的参数是否与 printf 样式格式字符串参数 my_format 保持一致。

__attribute__指定基础类型,数组,结构体

aligned

  • aligned (alignment)

aligend指定了最小对齐边界字节数,参数alignment必须是2的幂次方,例如1,2,4,8…当alignment为空时,等效于目标最大对齐边界(通常但不一定是8/16字节),指定参数会影响变量地址和占用内存两个属性。

aligned应用于基础类型变量和数组时

编译器对于基础类型变量和数组的编译原则如下:

  1. 系统默认按照变量类型本身占用内存的字节数来对齐边界(地址原则)
  2. 系统按照变量类型本身占用内存来分配内存字节数(内存原则)
  3. 通俗来讲,针对于基础类型变量和数组时,属性限定了变量存储的内存首地址,但是并没有影响变量实际占用内存的大小。

举例如下

#include <stdio.h>

int main(int argc, char *argv[])
{
    int x;
    int y __attribute__((aligned (16))) = 0;
    char z[15] __attribute__((aligned (16)));

    printf("sizeof(char) = %lu Bytes, sizeof(int) = %lu Bytes\r\n", sizeof(char), sizeof(int));
    printf("x.addr = %p, sizeof(x) = %lu Bytes\r\n", &x, sizeof(x));
    printf("y.addr = %p, sizeof(y) = %lu Bytes\r\n", &y, sizeof(y));
    printf("z[0].addr = %p, sizeof(z) = %lu Bytes\r\n", &z[0], sizeof(z));
    return 0;
}

/*实验结果
sizeof(char) = 1 Bytes, sizeof(int) = 4 Bytes
x.addr = 0x7ffea29250fc, sizeof(x) = 4 Bytes
y.addr = 0x7ffea29250f0, sizeof(y) = 4 Bytes
z[0].addr = 0x7ffea2925100, sizeof(z) = 15 Bytes
*/
/*分析过程
x:没有指定参数,等效于__attribute((aligned(sizeof(int)))),地址(0x7ffea29250fc)能被4整除,占用内存为4
y:指定了参数__attribute__((aligned (16))),地址(0x7ffea29250f0)能被16整除,占用内存为4
z:指定了参数__attribute__((aligned (16))),地址(0x7ffea2925100)能被16整除,占用内存为15
*/

aligned应用于结构体时

编译器对于结构体边界对齐的原则如下:

  1. 结构体整个占用字节默认按照结构体内最大基础变量类型占用字节数对齐(内存原则)
  2. 结构体内部的基础变量类型默认按照变量类型本身占用字节来对齐边界(地址和内存原则)
  3. 结构体变量的首地址默认按照结构体内最大基础变量类型占用字节数对齐边界(地址原则)
  4. 注意:结构体内有结构体时,需要拆分到最基础的数据类型
#include <stdio.h>
typedef struct F{char a[60];}F_T;
typedef struct F1{
    char i;
    short j;
    int k;
}F1_T;
typedef struct F2{
    char i;
    short j;
    int k;
}__attribute__((aligned(16))) F2_T;
typedef struct F3{
    char i;
    short j;
    int k;
    F_T f;
}__attribute__((aligned(16))) F3_T;
int main(int argc, char *argv[])
{
    F_T f;
    F1_T f1;
    F2_T f2;
    F3_T f3;

    printf("sizeof(char)=%lubytes, sizeof(short)=%lubytes, sizeof(int)=%lubytes\r\n", sizeof(char), sizeof(short), sizeof(int));
    printf("f.addr=%p, sizeof(F_T)=%lubytes\r\n", &f, sizeof(F_T));
    printf("f1.addr=%p, sizeof(F1_T)=%lubytes\r\n", &f1, sizeof(F1_T));
    printf("f2.addr=%p, sizeof(F2_T)=%lubytes\r\n", &f2, sizeof(F2_T));
    printf("f3.addr=%p, sizeof(F3_T)=%lubytes\r\n", &f3, sizeof(F3_T));

    return 0;
}
/*实验结果
sizeof(char)=1bytes, sizeof(short)=2bytes, sizeof(int)=4bytes
f.addr=0x7ffd9b9e85e0, sizeof(F_T)=60bytes
f1.addr=0x7ffd9b9e85c0, sizeof(F1_T)=8bytes
f2.addr=0x7ffd9b9e85d0, sizeof(F2_T)=16bytes
f3.addr=0x7ffd9b9e8620, sizeof(F3_T)=80bytes
*/
/*分析过程
F_T&f:没有指定参数,等效于typedef struct F{...}__attribute((aligned(sizeof(char)))) F_T;
    结构体占用内存为60*sizeof(char);
    首地址(0x7ffd9b9e85e0)能被sizeof(char)整除;
F1_T&f1:没有指定参数,等效于typedef struct F1{...}__attribute((aligned(sizeof(int)))) F1_T;
    结构体占用内存为8字节,因为char和short类型可以存放在一个int占用的内存里,且不违背变量对齐原则
    首地址(0x7ffd9b9e85c0)能被sizeof(int)整除
F2_T&f2:指定按照16字节对齐
    结构体占用内存: 必须是16的整数倍,结果为16字节
    首地址必须能被16整除: 地址(0x7ffd9b9e85d0)满足
F3_T&f3:指定按照16字节对齐
    结构体占用内存: 必须是16的整数倍,结果为80字节,满足要求
    首地址必须能被16整除: 地址(0x7ffd9b9e8620)满足要求
    注意事项:F3_T内部包含了一个F_T(占用60字节),分析时需要拆开结构体,看内部的基础类型
*/

packed

packed应用于结构体时

编译器对于packed属性原则如下:

  1. 系统应该为结构体成员按照最小的对齐方式来对齐边界(1bit或者1字节)

示例如下

#include <stdio.h>

typedef struct F1{
    char i;
    int j[2] __attribute__((packed));
}F1_T;
typedef struct F2{
    char i;
    int j[2] __attribute__((packed));
    int k;
}F2_T;
int main(int argc, char *argv[])
{
    F1_T f1;
    F2_T f2;

    printf("sizeof(char)=%lubtyes, sizeof(int)=%lubtyes \r\n", sizeof(char), sizeof(int));
    printf("f1.addr=%p, sizeof(F1_T)=%lubtyes \r\n", &f1, sizeof(F1_T));
    printf("f2.addr=%p, sizeof(F2_T)=%lubtyes \r\n", &f2, sizeof(F2_T));
    return 0;
}

/*实验结果
sizeof(char)=1btyes, sizeof(int)=4btyes 
f1.addr=0x7fffeb902240, sizeof(F1_T)=9btyes 
f2.addr=0x7fffeb902250, sizeof(F2_T)=16btyes 
*/
/*分析过程
F1_T&F1:仅仅指定结构体内部的一个参数受packed限定。
    首地址:由于结构体规则,首地址需要对齐结构体内基础类型最大占用内存字节数,这里是int,但是int由被packed限定了,所以地址按照1字节边界对齐。
    内存占用:由于结构体规则,占用内存字节需要能整除结构体内基础类型最大占用内存字节数,这里是int,但是int由被packed限定了,所以结构体能被1整除就行了,所以这里占用了9字节。
F2_T&F2:仅仅指定结构体内部的一个参数受packed限定。
    首地址:与F1_T对比,此结构体新增了int k成员,且sizoef(int)>1,所以地址需要保持与sizoef(int)边界对齐。
    内存占用:与F1_T对比,此结构体新增了int k成员,且sizoef(int)>1,所以占用内存需要能整除sizeof(int),所以这里占用了16字节。
*/

section

通常情况下,编译器将变量放置在data段/bss段。然而有时候,可能需要额外增加段,例如,映射到相关到硬件。通过section属性就可以指定到固定段空间。举例如下

struct duart a __attribute__ ((section ("DUART_A"))) = { 0 };
struct duart b __attribute__ ((section ("DUART_B"))) = { 0 };
char stack[10000] __attribute__ ((section ("STACK"))) = { 0 };
int init_data __attribute__ ((section ("INITDATA")));

参考列表

GCC 9.4 Manual

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

C语言之__attribute__ 的相关文章

随机推荐

  • js中数组和set的相互转化

    1 array gt set xff08 数组转set xff09 例子 xff1a span class token keyword let span array span class token operator 61 span spa
  • 驼峰命名规则

    驼峰规则 1 小驼峰式命名法 xff08 lower camel case xff09 xff1a 第一个单字以小写字母开始 xff0c 第二个单字的首字母大写 例如 xff1a firstName lastName 2 大驼峰式命名法 x
  • 机器人巡线算法优化方案

    引言 大赛机器人的运行环境是大赛的场地 比赛时场地的情况将非常复杂 因此 机器人必须知道自己当前的位置才能决定要执行怎样的动作 定位是大赛机器人各种性能发挥的基础 也是机器人全场路径自动规划的基础 目前 比较先进的机器人定位方法为陀螺导航
  • 如何在评论中以超链接的方式放置链接

    如何在评论中以超链接的方式放置链接 格式 xff1a url 61 链接 显示的内容 url 例子1 xff1a url 61 http blog csdn net qq 39189509 article details 73716422
  • STM32唯一ID(Unique Device ID)的读取方法

    每一个STM32微控制器都自带一个96位的唯一ID xff0c 也就是Unique Device ID或称为UID xff0c 这个唯一ID在任何情况下都是唯一的且不允许修改 在开发过程中 xff0c 可能需要用到这个UID xff0c 比
  • 设计算法以判断集合A是否是集合B的子集

    一 题目 xff1a 假设递增有序的带头结点的链表A B分别表示一个集合 xff0c 试设计算法以判断集合A是否是集合B的子集 xff0c 如是返回1 xff0c 否则返回0 二 思路 xff1a 1 A的值大于B的值 xff0c 那就A的
  • 将两个递增有序的带头结点的单链表A、B合并成为一个递增有序的带头结点的单链表

    一 题目 将两个递增有序的带头结点的单链表A B合并成为一个递增有序的带头结点的单链表 二 思路 将单链表A B遍历 xff0c 比较两个链表中元素的值 xff0c 分为下列情况 xff1a A lt B 则A的指针向后移一位 xff0c
  • 将链表L就地逆置,即利用原表各结点的空间实现逆置

    一 题目 将链表L就地逆置 xff0c 即利用原表各结点的空间实现逆置 二 思路 在链表的第二个元素开始执行逆置 xff0c 因为如果链表只有一个元素 xff0c 那么逆置就没有意义了 步骤 xff1a 假设原链表如下 xff1a 将结点1
  • Android| failed to connect to /10.0.2.2 (port 80) after 10000ms

    关于使用android模拟器访问本地服务器失败 第一种 xff1a 使用Google自带的模拟器 用http 127 0 1 1访问失败 描述 xff1a 明明在浏览器中使用http 127 0 1 1可以访问Apache本地服务器 xff
  • you-get【视频下载】

    如何安装you get 在目录C Users jia AppData Local Programs Python Python36 Scripts中shift 43 右键 xff0c 唤出命令行窗口 xff08 如果配置好了环境变量 xff
  • python中调用adb命令来控制手机

    前言 如今python是非常多人学习的 xff0c 而手机也几乎人手一部 对于很多Python学习者 xff0c 想用python来完成android手机中各种炫酷的的控制 xff0c adb是必不可缺少的工具之一 比如17年末大热的微信
  • 中断和中断Handlers

    任意一个操作系统的核心responsibility都是管理连接到机器上面的硬件 包括硬盘 键盘 鼠标 3D处理器等 为了满足这项responsibility xff0c 内核需要和机器本身通信 xff0c 假如说处理器在维度上比他访问的硬件
  • 从github下载项目(clone)

    1 首先去官网下载git xff0c 安装 xff08 基本都是默认 xff09 2 本地创建文件夹用来存放下载的项目 3 在第二个文件夹下打开 git bash here 4 复制地址 5 在bash下面输入 git clone 43 地
  • 结构体中有指针成员的逐层malloc 与 free

    1 malloc的时候注意 xff0c 如果结构体有结构体指针成员 xff0c malloc的时候需要逐层都进行malloc 先malloc最外面的event nodeP xff0c 才能访问到event nodeP gt dataP 和
  • 摄像头云台的设计,组装与使用方法

    摄像头云台的设计 xff0c 组装与使用方法 简介三维模型的设计实物组装接线配置环境编写程序实物效果 简介 本篇是摄像头云台的设计与使用方法 xff0c 用树莓派进行控制 xff0c 具体应用于我自制的多功能小车上边 xff0c 目前整个小
  • map文件查看

    map文件里面内容大致分为五大类 xff08 按照map文件分类的顺序 xff09 xff1a 1 Section Cross References xff1a 模块 段 入口 交叉引用 xff1b 2 Removing Unused in
  • AuthenticationProvider AuthenticationManager

    1 AuthenticationManager AuthenticationProvider UserDetailsService 2 多重认证 3 实现
  • docker之dockerFile

    在Dockerfile中用到的命令有 FROM FROM指定一个基础镜像 xff0c 一般情况下一个可用的 Dockerfile一定是 FROM 为第一个指令 至于image则可以是任何合理存在的image镜像 FROM 一定是首个非注释指
  • ubuntu如何安装最新版的npm

    使用 apt安装的npm总是因为版本过低报错 xff0c span class token function npm span ERR span class token operator span Linux 4 15 0 136 gene
  • C语言之__attribute__

    公众号 xff1a 嵌入式不难 attribute 声明函数属性 可以使用 attribute 来声明函数的属性 xff0c 这些属性可以帮助编译器优化调用或更仔细地检查代码的正确性 例如 xff0c 属性 noreturn 用来指定函数从