当前位置: 首页 > article >正文

16. 结构体占内存大小是怎么计算的,有哪些原则?

结构体的内存大小计算主要遵循以下原则:

  1. 每个成员类型的大小:每个成员的内存大小由它的类型决定。例如,int 通常是 4 字节,char 是 1 字节,double 是 8 字节,等等。
  2. 成员的对齐要求:系统对不同数据类型有对齐要求。例如,在 32 位系统中,int 通常要求 4 字节对齐,double 通常要求 8 字节对齐。这意味着该成员的起始地址必须是其大小的整数倍。
  3. 结构体的对齐要求:结构体的总大小必须是其最大对齐成员的倍数。这通常会导致在结构体末尾添加填充字节,以确保结构体的对齐符合最大成员的对齐要求。

1. 内存对齐原则

每个成员在结构体中的位置,必须满足它自身的对齐要求。编译器可能会在成员之间添加填充字节(padding),以保证每个成员的起始地址满足其对齐要求。

  • 对齐要求是指内存地址必须是某个数值的倍数,通常是 2 的幂。
  • 最后,结构体的大小需要是最大对齐的倍数。

2. 计算步骤

  • 逐一计算每个成员的大小。
  • 考虑对齐要求,并在必要时加入填充字节。
  • 最后,确保结构体的总大小是对齐要求的倍数。

3. 示例

示例 1:简单结构体
struct S1 {
    char c;   // 1 字节
    int i;    // 4 字节
    double d; // 8 字节
};
  • char c 占 1 字节,位于结构体的开始,偏移量为 0。对齐要求为 1 字节,因此无需填充。
  • int i 占 4 字节,要求 4 字节对齐。因为上一个成员 char 只占用了 1 字节,因此在 int 前需要填充 3 字节(地址 1 到 3),i 位于地址 4 到 7。
  • double d 占 8 字节,要求 8 字节对齐。上一个成员 int 占用 4 个字节,因此 d 可以直接存储在地址 8 到 15。

总内存布局如下:

字节偏移成员占用大小
0char c1 字节
1 - 3填充3 字节
4 - 7int i4 字节
8 - 15double d8 字节
  • 总大小为 16 字节。
  • 由于 double 是对齐要求最大的成员,结构体的总大小必须是 8 的倍数,所以没有额外的填充字节。
示例 2:更复杂的结构体
struct S2 {
    char c1;  // 1 字节
    char c2;  // 1 字节
    double d; // 8 字节
    int i;    // 4 字节
};
  • char c1 占 1 字节,偏移量 0。
  • char c2 紧接着 c1,占 1 字节,偏移量 1。
  • double d 要求 8 字节对齐,但上一个成员 char c2 的地址为 1,因此必须插入 6 字节的填充,使 d 的起始地址为 8。
  • int i 要求 4 字节对齐,d 占用了 8 到 15 字节,因此 i 从 16 开始,位于 16 到 19 字节。

内存布局如下:

字节偏移成员占用大小
0char c11 字节
1char c21 字节
2 - 7填充6 字节
8 - 15double d8 字节
16 - 19int i4 字节
  • 目前为止占用了 20 字节。
  • 最大对齐单位是 double(8 字节),因此结构体的大小必须是 8 的倍数,最后需要添加 4 字节的填充,使总大小达到 24 字节。
示例 3:位域的结构体
struct S3 {
    int a : 4; // 位域,占用 4 位(0.5 字节)
    int b : 4; // 位域,占用 4 位(0.5 字节),可以与 a 共用一个字节
    int c : 8; // 位域,占用 8 位(1 字节)
    double d;  // 普通成员,占用 8 字节
};
  • ab 可以共享一个字节,存储在字节 0。
  • c 占用 1 字节,存储在字节 1。
  • double d 需要 8 字节对齐,因此需要从字节 8 开始,因此会有 6 字节的填充。

内存布局如下:

字节偏移成员占用大小
0a:4 + b:41 字节
1c:81 字节
2 - 7填充6 字节
8 - 15double d8 字节
  • 总大小为 16 字节。

4. 总结计算原则

  1. 逐一计算每个成员的大小和对齐要求
  2. 对齐补齐:如果一个成员的起始地址不满足对齐要求,则需要填充字节来对齐它。
  3. 结构体对齐:结构体的总大小应是其最大成员的对齐要求的倍数,因此需要在结构体末尾填充字节。

5. 注意点

  • 平台相关性:不同平台对不同类型的对齐要求可能不同,因此同一个结构体在不同平台上的大小可能不同。
  • 位域的实现依赖于编译器:位域的存储和对齐可能因编译器的不同而不同,位域的使用需要考虑到目标平台的实现细节。

http://www.kler.cn/a/282231.html

相关文章:

  • 鸿蒙中如何实现图片拉伸效果
  • Linux Android 正点原子RK3568替换开机Logo完整教程
  • 深入List集合:ArrayList与LinkedList的底层逻辑与区别
  • 逆向攻防世界CTF系列37-crackme
  • 不用来回切换,一个界面管理多个微信
  • 【HAProxy09】企业级反向代理HAProxy高级功能之压缩功能与后端服务器健康性监测
  • OJ-0829
  • Python 中的 `and`, `or`, `not` 运算符:介绍与使用
  • Linux进程间的通信(二)管道通信及其实际应用(主要是实际编程应用,底层涉及不太多,想了解底层参考《UNIX环境高级编程》)
  • C++ QT 单例模式
  • uniapp秋云图表报错json underfind的原因
  • 【C#】【EXCEL】Bumblebee/Components/Analysis/GH_Ex_Ana_CondBetween.cs
  • 《python语言程序设计》2018版第8章第6题统计字符串中的字母个数
  • C#实现文件的上传
  • 华为AR路由使用PPPoE获取IPv6地址上网
  • 软件工程基础知识(3)
  • 【C++】汇编分析
  • 【软件工程】软件工程
  • Oracle(87)如何判断是否需要重建索引?
  • 计算机毕业设计选题推荐-救援物资管理系统-Java/Python项目实战
  • numpy 中的降维与升维
  • 编程路上的“迷宫逃脱”:从Bug堆到算法之巅的奇妙之旅
  • Renesa Version Board和微信小程序通信
  • sqli-labsSQL手工注入第26-30关
  • scrapy--图片管道-ImagesPipeline
  • 第十五章、 Io流