【C语言高阶篇】结构体 —— 什么是内存对齐?

C/C++
163
0
0
2024-02-25

⛺️生活的理想,就是为了理想的生活!

文章目录
  • 前言
  • 💬 结构体内存对齐
  • 💭 ofsetof 宏的应用
  • ✅ 结构体的内存对齐规则一
  • ✅ 结构体的内存对齐规则二
  • ✅ 结构体的内存对齐规则三
  • ✅ 结构体的内存对齐规则四
  • 💬 结构体内存对齐练习
  • 💭 练习一
  • 💭 练习二
  • 💭 练习三
  • 总结
💬 结构体内存对齐
⛳️大家可能会想结构体内存对齐这部分知识是干嘛用的呢?不要急,接下来就给大家 演示一下这部分内容到底能干嘛? 📚 代码演示:
#include<stdio.h>
struct S1
{
	char c1;//1
	int i;//4
	char c2;//1
};
struct S2
{
	int i;//4
	char c1;//1
	char c2;//1
};

int main()
{
	printf("%d", sizeof(struct S1));
	printf("%d", sizeof(struct S2));
	return 0;
}
🌈在我们没学结构体内存对齐这部分知识前,我们肯定是按照以前计算内存大小的办法计算的。
  • printf(“%d”, sizeof(struct S1)); 计算类型大小而 S1 里面的类型有:
  • int char char 这三个加起来的大小不就是 4+1+1
  • 6 个字节嘛!结构体 S2S1
  • 包含的类型都是一样的那也就是 6 个字节了,可真的是这样吗?

📑 代码结果:

请添加图片描述

📝 诶这里S1的类型大小居然是 12,S2的类型大小居然是 8。
  • 啊为什么会这样啊?这里就要用到结构体的内存对齐这个知识点了!
💭 ofsetof 宏的应用
📑 ofsetof是什么意思呢?
  • 它是用来计算结构体成员相较于起始位置的偏移量!
  • 在使用之前需要引入头文件 #include <stddef.h>

在这里插入图片描述

📖 问题引入:那么S1结构体每个元素的偏移量是多少?

  • 我们来用offsetof库函数来看一下

📚 代码演示:

#include <stdio.h>
#include <stddef.h>
struct S1
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%d\n", offsetof(struct S1, c1));
	printf("%d\n", offsetof(struct S1, i));
	printf("%d\n", offsetof(struct S1, c2));
	return 0;
}

📑 代码结果:

在这里插入图片描述

⛳️ 这里我们就可以看到结构体里面每个元素,相较于起始位置的偏移量
  • 🌱 那么我们就可以大概画出,结构体S1大概在内存中的存储位置
  • 🌱 假设我们结构体是从红箭头的位置开始存储的!

在这里插入图片描述


⛳️ 这里哦!是不是就很直观呢?而画出来的结构体s1所占字节

  • 🌱 刚好是8个字节和我们前面计算的一模一样!
  • 🌱 这是为什么呢?
  • 🌱 这是因为结构体在存储是是有一定规则的
  • 🌱 下面就给大家讲讲结构体的内存对齐规则
✅ 结构体的内存对齐规则一


⛳️ 第一个成员在与结构体变量偏移量为0的地址处。
  • 🌱 也就是只要是在结构体里面的第一个成员。
  • 🌱 他都存储在结构体变量偏移量为0的地址处。

📑图片展示:

在这里插入图片描述

✅ 结构体的内存对齐规则二


⛳️ 其他成员变量要对齐到某个数字 (对齐数) 的整数倍的地址处。
  • 🌱 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的值为8
  • Linux中没有默认对齐数,对齐数就是成员自身的大小

📜说明:

⛳️ 这是什么意思呢?拿上一个结构体参考

  • 🌱 假如我们第一个成员类型为1 ,第二个为4
  • 🌱 而VS中默认的值为 8, 4和 8比
  • 🌱 取他们之间的较小值4为 对齐数
  • 🌱所以第二个成员应该放在 (对齐数) 4的整数倍的地址处。

📑图片展示:

在这里插入图片描述

✅ 结构体的内存对齐规则三
⛳️ 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  • 🌱 也还是拿上面的结构体举例,前两个成员我们已经确定内存了
  • 🌱 而第三个是 char 一个字节,那么岂不是放上一个字节就对啦!
  • 🌱 那么这就和我们计算的12个字节完全不一样了!

📚 其实最后一个成员就是这样存储的,但是由于:

  • 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  • 而这个结构体 3 个成员类型分别是 1 4 1,那么
  • 结构体总大小就是 4的倍数 12 所以最后 3个字节也会算进去

在这里插入图片描述

✅ 结构体的内存对齐规则四
⛳️ 在讲规则四之前我们需要计算这结构体的存储字节以及:
  • 结构体每个元素的对齐数是多少?

📚 代码演示:

struct S3
{
	double d;
	char c;
	int i;
};
⛳️相信只要把前三个规则掌握了那么计算这个结构体每个成员 对奇数 其实很简单!我们来一起算一下
  • double的存储字节是8 VS的默认对齐数是8
  • 那么区最小值也是 8
  • char的存储字节是1 VS的默认字节是8
  • 那么取最小值就是 1
  • int 的存储字节是4 VS的默认字节是8
  • 那么取最小值就是 4

🔥 而结构体的大小数所有成员最大的整数倍,那么就是8的倍数!知道这些算结构体的大小还不简单

📑图片展示:

在这里插入图片描述

而如果嵌套了结构体的情况该怎么计算呢?

⛳️ 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

📚 代码演示:

#include <stdio.h>
#include <stddef.h>
struct S3
{
	double d;
	char c;
	int i;
};

struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

int main()
{
	printf("%d\n", sizeof(struct S4));
	return 0;
}
⛳️ 这时候就需要结构体的内存对齐规则四了:
  • 🌱 当嵌套了结构体时,嵌套的结构体对齐到自己的最大对齐数的整数倍处
  • 🌱 结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。 🌱 我们来带着大家算一下

US3的最大对齐数是 8 ,所以Ustruct S3 s3;就应该对齐到自己最大的对齐数的整数倍

  • 而结构体s3的大小是 16 个字节所以这里就会申请 16 个字节存放s3。
  • 存储 s3 ,而double也是8个字节所以下一个对齐数也是 8 的倍数
  • 那么这样不就好计算起来了!

📑图片展示:

在这里插入图片描述

📜 所以这里结构体S4的大小就是32个字节大家可以运行看一下呢?看看看和博主算的对不对!
💬 结构体内存对齐练习
⛳️好了上面的四个规则我们学完了就可以过来做一下练习来巩固一下自己所学习的知识 ,俗话说学习学习,光学不练有什么用呢?
💭 练习一


⛳️在VS2013下,默认对齐数为8字节,这个结构体所占的空间大小是( )字节
typedef struct{
  int a;
  char b;
  short c;
  short d;
}AA_t;
💭 练习二


⛳️在VS2013下,默认对齐数为8字节,这个结构体所占的空间大小是( )字节
struct tagTest1
  {
    short a;
    char d; 
    long b;   
    long c;   
  };
  struct tagTest2
  {
    long b;   
    short c;
    char d;
    long a;   
  };
  struct tagTest3
  {
    short c;
    long b;
    char d;   
    long a;   
  };
  struct tagTest1 stT1;
  struct tagTest2 stT2;
  struct tagTest3 stT3;

  int main()
  {
  		printf("%d %d %d", 
			  sizeof(stT1), 
			  sizeof(stT2), 
			  sizeof(stT3));
  		return 0;
 }
💭 练习三


⛳️在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是( )
struct A
{
	int a;
	short b;
	int c;
	char d;
};

struct B
{
	int a;
	short b;
	char c;
	int d;
};
总结

✅ 归纳: 好了以上就是关于结构体的内存对齐的全部知识点了,学会这些计算结构体的大小还不是手到擒来呢结构体的内存对齐规则一结构体的内存对齐规则二结构体的内存对齐规则三结构体的内存对齐规则四知识点练习 ☁️ 把本章的内容全部掌握,那么恭喜你又距离编程大牛又进了一步!