C语言的结构体内存对齐

C语言的结构体内存对齐,第1张

我们深入探讨一下C语言结构体内存的大小,无论考研还是找工作这都是一个非常热门的一个考点
讲解之前我们先看一下这样一段代码:

struct S1{
	char c1;
	int num;
	char c2;
};
struct S2{
	char c1;
	char c2;
	int num;
};

大家认为这两个结构体分别占了多大的内存,是否是简单的两个char类型加上一个int类型占的大小。
我们可以测试一下最终的结果是否是我们想象的那样

很明显结构体的大小并不是简单的类型字节数相加那么简单,那么结构体到底是如何在内存中存放的,我们来探讨一下。
结构体在内存中的存放是由一定的规则的,例如:

  1. 结构体的第一个成员直接对齐到相对于结构体变量起始位置为0的偏移处
  2. 从第二个成员开始,要对齐到某个对齐数的整数倍的偏移处。(对齐数:结构体成员自身大小和默认对齐数的较小值)。vs编译器默认对齐数是8。Linux环境默认不设对齐数(对齐数是结构体成员的自身大小)。

    num是整形占4个字节,默认对齐数是8,取较小值,4是4的整数倍,所以num的起始位置放在偏移量为4的地址中。第三个变量c2占1个字节,加起来就是9个字节啊,为什么结果是12个字节呢?这就涉及到我们结构体的第三个规则了


3.结构体的总大小,必须是最大对齐数的整数倍,每个结构体成员都有一个对齐数,其中最大的对齐数就是最大对齐数。而我们最大的对齐数的成员变量是num,那么最大对齐数就是4,而很明显9不是4的整数倍,所以要变成12个字节。

蓝色区域的内存就被浪费了。我们可以做个实验,有一个库函数是offsetof(),它可以查看结构体成员距离结构体的起始位置,头文件是

struct S1 {
	char c1;
	int num;
	char c2;
};
struct S2 {
	char c1;
	char c2;
	int num;
};
#include 
int main() {
	/*printf("%d\n", sizeof(struct  S1));
	printf("%d\n", sizeof(struct  S2));*/
	printf("%d\n", offsetof(struct S1, c1));
	printf("%d\n", offsetof(struct S1, num));
	printf("%d\n", offsetof(struct S1, c2));
	


	return 0;
}

我们由此可以得出S2在内存中的存储方式

看完上面两个结构体我们知道了结构体的存储方式,那我们再看一下下面这个结构体:

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

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

这个S4结构体的内存占多少字节呢?我们要先计算一下S3结构体的大小。
d为第一个成员,占了8个字节,c成员占偏移量为8的第九个字节,剩下的i为4个字节,只能占偏移量为12个字节的地址,总大小是16个字节。

要计算S4的大小,我们要先了解一下结构体存储的第四个规则
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处。结构体的整体大小就是最大对齐数(含嵌套结构体的对齐数)的整数倍。

S4结构体第一个成员c1为1个字节,占据偏移量为0的空间,而第二个成员是S3结构体,S3结构体内最大对齐数是d,为8个字节,所以S3的偏移量就是8。S4第三个结构体成员d对齐数是8,刚好可以放在偏移量为24的空间里,这样算下来正好是32个字节,S4最大对齐数为8,32正好是8的倍数,不用补齐,所以S4的结构体大小就是32个字节。

为什么存在内存对齐:

  1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能
    在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的
    内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总体来说:结构体的内存对齐是拿空间来换取时间的做法。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/3002886.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-27
下一篇 2022-09-27

发表评论

登录后才能评论

评论列表(0条)

保存