文章目录

  • 结构体内存对齐规则
  • 结构体大小计算 - 三步曲
  • 为什么存在内存对齐?
  • 设计结构体时的技巧
  • 修改默认对齐数

结构体内存对齐规则

我们知道,整型变量有自己的大小,浮点型变量有自己的大小,数组也有自己的大小,那么结构体有没有自己的大小呢?
回答是肯定的,结构体也有自己的大小,但是结构体的大小并不是简单地将每个结构体成员的大小相加就能得到。

结构体的大小计算遵循结构体的对齐规则

  1. 第一个成员在与结构体变量偏移量为0的地址处。(即结构体的首地址处,即对齐到0处)
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
  3. 结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

对齐数 = 该结构体成员变量自身的大小与编译器默认的一个对齐数的较小值。
注:VS中的默认对齐数为8,不是所有编译器都有默认对齐数,当编译器没有默认对齐数的时候,成员变量的大小就是该成员的对齐数。

结构体大小计算 - 三步曲

知道了结构体内存对齐规则,我们就可以计算结构体的大小了。计算结构体的大小可分为三个步骤。我们拿下面这个结构体举例:

struct S
{double d;char c;int i;
};

第一步:找出每个成员变量的大小将其与编译器的默认对齐数相比较,取其较小值为该成员变量的对齐数。

注:我使用的是VS编译器,故默认对齐数为8。

第二步:根据每个成员对应的对齐数画出它们在内存中的相对位置。

第三步:通过最大对齐数决定最终该结构体的大小。

通过图我们可以知道,绿色部分(double d成员占用)+红色部分(char c成员占用)+紫色部分(int i成员占用)+红色与紫色之间的白色部分(浪费掉了)总共占用了16个字节的内存空间。
我们需要将它们总共占用的内存空间(16)与结构体成员的最大对齐数(8)相比较,结构体的总大小为最大对齐数的整数倍,此时16正好是8的整数倍,所以该结构体在VS编译器下的大小就16个字节。即创建一个该类型的结构体变量,内存需为其开辟16个字节的内存空间。

注意:大多数情况下,成员变量已经占用的总字节个数并不一定正好为其成员变量中的最大对齐数的整数倍,这时我们需要将其扩大为最大对齐数的整数倍。

为什么存在内存对齐?

平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些平台只能在某些地址处取得某些特定类型的数据,否则抛出硬件异常。
比如,当一个平台要取一个整型数据时只能在地址为4的倍数的位置取得,那么这时就需要内存对齐,否则无法访问到该整型数据。

性能原因: 数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐内存,处理器需要作两次内存访问;而对齐的内存访问仅需一次。

在画图时可能有博友会想,内存这么重要,在进行内存对齐的时候怎么还有内存被白白浪费掉呢?
现在看来,其实结构体的内存对齐是拿空间来换取时间的做法。

设计结构体时的技巧

其实在我们设计结构体的时候,如果结构体成员的顺序设计得合理的话,是可以避免不必要的内存消耗的。
两个结构体的成员变量相同,但是成员变量的顺序不同,可能就会出现结构体的大小不同的情况:

struct S1
{char a;char b;int c;
};//结构体1
struct S2
{char a;int c;char b;
};//结构体2

我们可以看到,结构体1和结构体2的成员变量一模一样,可是当我们按照内存对齐规则来计算两个结构体的大小的时候,会发现两个结构体的大小不一样,在VS编译器下第一个结构体大小为8,第二个结构体大小为12。

可以见得,结构体成员变量的顺序不同,可能会造成内存不必要的损失。将占用空间小的成员尽量集中在一起,可以有效地避免内存不必要的浪费。

修改默认对齐数

要修改编译器的默认对齐数,我们需要借助于以下预处理命令:

#pragma pack()

如果在该预处理命令的括号内填上数字,那么默认对齐数将会被改为对应数字;如果只使用该预处理命令,不在括号内填写数字,那么会恢复为编译器默认的对齐数。

#include <stdio.h>#pragma pack(4)//设置默认对齐数为4
struct S1
{char a;//1/4->1int b;//4/4->4char c;//1/4->1
};//12
#pragma pack()//取消设置的默认对齐数,还原为默认#pragma pack(1)//设置默认对齐数为1
struct S2
{char a;//1/1->1int b;//4/1->1char c;//1/1->1
};//6
#pragma pack()//取消设置的默认对齐数,还原为默认int main()
{printf("%d\n", sizeof(struct S1));//打印结果为12printf("%d\n", sizeof(struct S2));//打印结果为6return 0;
}

于是,当结构体的对齐方式不合适的时候,我们可以自己更改默认对齐数。

结构体内存对齐(如何计算结构体的大小)相关推荐

  1. 结构体及结构体内存对齐讲解

    文章目录 1.结构体类型的声明 2.结构体变量的定义和初始化 3.结构体内存对齐 4.结构体传参 1.结构体类型的声明 结构的基础知识 结构是一些值的集合,这些值称为成员变量.结构的每个成员可以是不同 ...

  2. c语言 char转int_图文并茂,一文讲透C语言结构体内存对齐

    ↑点击上方蓝色字体,关注"嵌入式软件实战派"获得更多精品干货. (以下有约5000字内容,建议收藏再读,推荐下载源码自行测试以加深理解.) 面试官:你知道C语言的结构体对齐吗? 应 ...

  3. 【C语言】一文搞定如何计算结构体的大小----结构体内存对齐规则

    要搞定如何正确地计算一个结构体的大小,我们就要深刻理解结构体内存对齐规则: 结构体内存对齐规则: 1.结构体的第一个成员永远在结构体起始位置偏移量为0的位置: 2.结构体成员从第二个成员开始,总是放在 ...

  4. 细讲C语言结构体(结构体内存对齐你懂了吗?)

    结构体 结构体类型的声明 结构体自引用 结构体变量的定义和初始化 结构体内存对齐 结构体传参 结构体 结构体的声明 结构体是一些值集合的,里面可以包括char,int,double等等的各种类型构成的 ...

  5. 语言结构体在内存的分布_结构体内存对齐,这篇文章给你彻底搞会!(干货收藏)...

    脚本之家 你与百万开发者在一起 公众号:C语言编程 作者:薛定谔的coding猫 一.内存对齐的原因  1.平台原因(移植原因):一些资料上是这样说的,"不是所有的硬件平台都能访问任意地址上 ...

  6. c#往结构体里面读数据_结构体内存对齐,这回给你彻底搞会!

    一.内存对齐的原因  1.平台原因(移植原因):一些资料上是这样说的,"不是所有的硬件平台都能访问任意地址上的任意数据:某些硬件平台只能在某些特定地址处取某些特定的数据,否则就会抛出硬件异常 ...

  7. printf打印结构体_工程师:这道题80%初学者都没做对!你确定搞懂结构体内存对齐了?...

    这是工程师面试后的实际经历-- 这道经典.易错的关于C语言结构体内存对齐的题目,你真的会吗: 求32bit环境下以下结构体所占的字节数:typedef struct test_struct{ char ...

  8. 结构体内存对齐,默认对齐数,结构体传参

    我们先来计算一下结构体内存的大小 现在我们计算一下stu1和stu2每个成员内存偏移是多少. 在介绍偏移量之前,我们先简单介绍一下offsetof(是一个宏),它是用来计算结构体成员相较于起始位置的偏 ...

  9. 【APUE】Chapter17 Advanced IPC sign extension 结构体内存对齐

    17.1 Introduction 这一章主要讲了UNIX Domain Sockets这样的进程间通讯方式,并列举了具体的几个例子. 17.2 UNIX Domain Sockets 这是一种特殊s ...

  10. 一文搞懂结构体内存对齐

    结构体声明 以及结构体变量创建 为什么会有内存对齐 如何计算内存对齐 在现实生活中 为了描述复杂个体 如 书本 人 单一的数据类型已无法满足我们的需要 这时候 自定义类型 结构体 孕育而生 结构是一些 ...

最新文章

  1. python实现新闻网站_如何用 100 行 Python 代码实现新闻爬虫?这样可算成功?
  2. JavaScript 笔记Day1
  3. django异常日志_5分钟教你学会Django系统错误监控
  4. Gradle Build速度加快终极方法(android studio)
  5. python 持续集成部署_Jenkins部署git+python项目实现持续集成
  6. 【视觉动效】全网最强解析播放器(自定义修改)源码
  7. C++ 引用和指针有什么区别?
  8. vex夹球机器人_汕头市4名中学生获2020年VEX机器人世锦赛初中组亚军
  9. 吴恩达机器学习:偏差与方差、欠拟合与过拟合
  10. 15.软件架构设计:大型网站技术架构与业务架构融合之道 --- 技术架构与业务架构的融合
  11. JSP详细教学新手必看
  12. 工程项目进度控制的主要措施有哪些?
  13. UI 自动化测试在有赞的实践
  14. c语言谷歌坐标转百度坐标,百度经纬度和google经纬度转换测试
  15. Revisit Knowledge Distillation: a Teacher-free Framework
  16. Golang可能会踩的58个坑之高级篇
  17. Java11完全兼容Java8吗_Java11正式发布,要不要升级请看这里!
  18. 4AT已经衰老 6AT正值壮年
  19. 获取url地址栏后面的参数
  20. 从蓝桥杯丢盔卸甲,来学一点javaScript

热门文章

  1. IPsec:strongswan与vpp实现ipsec
  2. 计算机硬盘序列号有什么意义,硬盘序列号会/为什么会改变
  3. aTrust项目的相关操作与分享
  4. Windows API函数大全(1)
  5. Windows程序设计:一个简单的API函数调用实例
  6. centos7字体颜色改变_CentOS7.3中设置Shell终端文本外观自定义字体
  7. springdata elasticsearch aggregation 操作
  8. 2021 9.14 p.m.小结 以及 数独问题探索(T3)
  9. vcf通讯录转excel
  10. 仿链家地图找房_【前端-自如/链家/安居客-地图找房】地图api如何获取浏览器视野范围内的经纬度区间?...