嵌入式C语言之结构体对齐详解

引言

https://m.toutiao.com/is/JctcUfB/

结构体作为日常开发中使用最广泛的数据类型之一,其重要性不言而喻 。上一篇我们介绍了结构体的定义, 变量的初始化, 以及结构体元素的访问方式。 今天我们再更深入的研究下结构体对齐的相关问题, 这也是很多小伙伴在编写代码中常常忽视的细节,从而导致各种各样的bug, 同时也是很多面试和笔试中经常遇见的问题之一。let's go~

结构体的大小

这里我们定义一个结构体,如下:

//在32位系统中:struct test{char a; //1字节int c; //4字节};struct test t1;

看到这里,我想请问下大家,结构体变量t1所占内存空间大小是多少呢? 是1+ 4 = 5字节嘛?

我们来测试下:

int main(void){printf('sizeof(t1) = %d.\n', sizeof(struct test));    return 0;}

编译运行:

结果居然是8!是否和大家想的一样呢? 为什么会这样呢, 明明这个结构体中元素a(char类型, 占1个字节) 和 元素b(int类型,占4个字节), 而最终结构体的大小却是8呢?

结构体对齐

这是因为结构体要考虑元素的对齐访问,所以导致每个元素实际占的字节数和字节本身的类型所占的字节数不一定完全一致。 好比如上面的例子, 其实就是4字节对齐访问(当然以多少字节对齐,是可以自己手动设置的,文章后面有详细介绍), 先上图,感受下:

看图理解: 首先元素a占用一个字节(一个格子,绿色部分),而这一排的其余三个格子(地址) 因为装不下元素b(占4个字节,即格子), 因此这三个格子被空闲(但其空间是属于结构体的本身的),接着从0x8地址开始装元素b(4个格子,即红色部分), 因此是该结构体变量t1最终占用8字节大小的内存空间。

趁热打铁: 来个练习题,加深下理解:

#include <stdio.h>//默认还是以4字节对齐struct test1{char a; short b; short c; char d[3]; int e; };int main(void){struct test1 t2;//打印结构体所占内存大小printf('sizeof(t2) = %d.\n', sizeof(struct test1));return 0;}

分析:首排盒子(4个小格子)先装下元素a(1个格子),还剩余3个格子,可以装下元素b(占2个格子), 剩余的一个格子已经装不下元素c(占2个格子)了, 因此启用新的大盒子,首先来装下元素c(2个格子), 然后剩余2个格子,我们可以再装元素d[0], d[1],接着再启用新的大盒子来装元素d[2], 此时剩余3个格子,不能装下元素e(4个格子), 因此又得启用新的大盒子,刚好完整装下。 最终盒子,我们共启用了4个大盒子,即4*4(小格子) = 16字节, 上图:

编译运行:

设置对齐指令

以多少字节对齐是由编译器来管理的,因此我们可以通过一些编译器指令来进行设置。这里我们以gcc编译器为例:

第一种:

一般C语言编译都支持该指令,如gcc ,ARMCC编译器:

//设置以n字节对齐#pragma pack(n)//这个区间内的对齐参数就是n。#pragma pack()

这种写法是以#prgama pack(n)开头, 并以#prgama pack()结尾,在此区间的代码都是按照n为对齐访问。通过例子来感受下:

#include <stdio.h>//设置成1字节对齐#pragma pack(1)struct test{char a;int c;};#pragma pack()int main(void){struct test t1;printf('sizeof(t1) = %d.\n', sizeof(struct test));return 0;}

上面的例子在文章开头时运行的结果是8,现在通过#pragma pack(1) 设置成1字节对齐访问,来看看运行结果:

结果是5,与我们默认4字节对齐运行结果(8)不一样哦,现在大家应该能明白其中的缘由了吧。

第二种:

其实也是现在使用更多,更被推荐的对齐指令(针对特定的数据类型):

//设置以n字节对齐__attribute__((aligned(n)))//取消以n字节对齐__attribute__((packed)) 

注意: __attribute__((packed))使用时直接放在要进行内存对齐的类型定义的后面,然后它起作用的范围只有加了这个东西的这一个类型。packed的作用就是取消对齐访问。通过例子感受下:

#include <stdio.h>//#pragma pack(1) __attribute__((aligned(1))) struct test1{char a; short b; short c; char d[3]; int e; }__attribute__((packed));//#pragma pack()int main(void){struct test1 t2;printf('sizeof(t2) = %d.\n', sizeof(struct test1));return 0;}

编译运行结果:

我们通过__attribute__((aligned(1)))设置1字节对齐,即结构体变量所占内存空间: a(1字节) + b(2字节) + c(2字节) + d(三个元素,3字节) + e(4字节) = 12字节,完全一致。

思考和总结

为什么要设置对齐访问,到底哪些情况需要设置和应该设置成多少字节对齐呢?

其实对齐访问主要原因是为了配合硬件,也就是说硬件本身有物理上的限制,如果对齐排布和访问会提高效率,否则会大大降低效率。例如在lcd驱动代码中,常常会将相关缓存数据会设置512或更多字节对齐,以达到更好的刷新频率。 总结来说是以空间换取性能,使硬件(如MMC, Cache等硬件)达到更高的速度和效率。

(0)

相关推荐

  • (7条消息) C语言

    C语言结构体对齐步骤: 结构体各成员对齐. 结构体总体对齐 C语言结构体对齐规则: 结构体(struct)的数据成员,第一个数据成员存放的地址为结构体变量偏移量为0的地址处. 其他结构体成员自身对齐时 ...

  • 嵌入式C语言之——结构体对齐详解

    嵌入式C语言之--结构体对齐详解 原创嵌入式笔记v2020-04-06 23:02:22 引言 结构体作为日常开发中使用最广泛的数据类型之一,其重要性不言而喻 .上一篇我们介绍了结构体的定义, 变量的 ...

  • c语言中结构体成员变量使用“.”符号赋值方法

    在C语言中定义结构体类型的变量时,其内部的成员变量赋值除常规的方法外,也可以通过点号"."进行赋值,如下图所示: 备注:在Linux系统上使用gcc / g++编译器调试通过,其它 ...

  • 【C语言】结构体占用字节数及存储与空间分配

    我们都知道在数据类型中,char类型占1个字节,short占2个字节,int占4个字节,long占8个字节等等. 在计算结构体大小时需要考虑其内存布局,结构体在内存中存放是按单元存放的,每个单元多大取 ...

  • C语言之结构体就这样被攻克了!

    「本文目录」 结构体的声明与定义 声明 定义 访问结构体成员 初始化结构体 对齐 结构体嵌套 结构体数组 结构体指针 传递结构体信息 传递结构体变量 传递指向结构体变量的指针 动态申请结构体 实战:建 ...

  • C语言之结构体就这样被攻克了!(绝对值得收藏的文章)

    来源 :公众号"TechZone",作者:HarrisWilde 「本文目录」 结构体的声明与定义 声明 定义 访问结构体成员 初始化结构体 对齐 结构体嵌套 结构体数组 结构体指 ...

  • 结构素描步骤详解(专业老师经验总结)

    结构素描黑白灰关系明确后, 各部分丰富起来, 尤其是交界线和灰部要变化丰富. 我们总结前人学习素描的经验,概括为下面三步走: 第一步:构图与打形,好比地基与骨架,地基决定上层建筑,骨架决定房子形状. ...

  • 常用结构做法图文详解

    来源:平法识图与钢筋算量 如有侵权请联系删除 结构工程细部节点做法 一.规定做法 01直螺纹丝头加工做法 说明: 1.直螺纹丝头加工前,钢筋端头采用专用机具切割. 2.加 工成型的有效丝 头长度 == ...

  • C语言经典编程(浙大版C语言第三版)详解

    C语言经典编程详解 -- 一览表: 1.[C语言经典编程]练习2-1 Programming in C is fun! (5分) 2.[C语言经典编程]练习2-3 输出倒三角图案 (5分) 3.[C语 ...

  • 【知识】C语言随机数生成,C语言rand和srand用法详解

      在实际编程中,我们经常需要生成随机数.例如,贪吃蛇游戏中在随机的位置出现食物,扑克牌游戏中随机发牌. 在C语言中,我们一般使用 <stdlib.h> 头文件中的 rand() 函数来生 ...