C/C++中结构体占用内存大小的计算方法

本文详细介绍了C语言中结构体内存对齐的基本概念及其计算规则,并通过具体实例展示了不同变量排列顺序如何影响结构体的内存占用。

引言


结构体在C语言中虽然经常使用,但是怎么计算一个结构体占用多大的内存,很多C语言的新手都没注意过,其实C语言的语法简单,难就难在它更偏向于与底层,与内存打交道。对于嵌入式方面来说,对C语言的要求更高,因为有些硬件的内存并不像我们使用的电脑的内存那么充裕,所以需要节约内存。

结构体中同样的变量个数,却可能导致占用内存的大小不同。


例子1:

#include <stdio.h>
int main()
{
	typedef struct a
	{
		int c;
		double d;		     
		char b[10];
	}test;
	test  e;
	printf("%d", sizeof(test));
	return 0;
} 
输出的结果为:32


例子2:

#include <stdio.h>
int main()
{
	typedef struct a
	{
		double d;
		int c;		     
		char b[10];
	}test;
	test  e;
	printf("%d", sizeof(test));
	return 0;
} 


输出的结果为:24

只是调换了一下int c和double d的位置,就导致了前者比后者多占用了8个字节的内存。这只是一个小例子。那么,结构体中到底怎么计算内存的呢。

计算规则

首先需要介绍<b>有效对齐值</b>:每个平台上的编译器都有默认对齐系数n,可以通过#pragma pack(n)来指定。有效对齐值就等与该对齐系数和结构体中最长的数据类型的长度两者最小的那一个值,比如对齐系数是8,而结构体中最长的是int,4个字节,那么有效对齐值为4。

结构体的内存计算方式遵循以下规则:

1.数据成员对齐规则:第一个数据成员放在offset为0的地方,以后的每一个成员的offset都必须是该成员的大小与有效对齐值相比较小的数值的整数倍,例子1中第一个数据成员是int 型,第二个是double,有效对齐值为8,所以double的起始地址应该为8,所以第一个int加上内存补齐用了8个字节

2.结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部有效对齐值的整数倍地址开始存储。(比如struct a中存有struct b,b里有char, int, double,那b应该从8的整数倍开始存储)

3.收尾工作:结构体的总大小,必须是其有效对齐值的整数倍,不足的要补齐。

有了这些规则,再来看上面的两个例子。

例子1中,int为第一个数据成员,所以下一个存储的起始位置是4,但是double占8个字节,按规则一,不满足8的倍数,填充到16,char一共有10个字节(数组在内存中的表示会分解成单个的),即总共26,进行收尾工作,有效对齐值为8,26不满足8的倍数,填充到32。

例子2中,double为第一个数据成员,所以下一个存储的起始位置是8,按规则一,占用了12个字节,char有10个字节,一共用了22个字节,进行收尾工作,该结构体中有效对齐值为8,22不满足8的倍数,填充到24。

例子

下面给你一个question,考察你是否懂了上面的规则

#include <stdio.h>
#pragma pack(8)
int main()
{
	struct Test
	{
		int a;
		//long double大小为16bytes
		long double b;		     
		char c[10];
	};
	printf("%d", sizeof(Test));
	return 0;
} 


#include <stdio.h>
#pragma pack(16)
int main()
{
	struct Test
	{
		int a;
		//long double大小为16bytes
		long double b;		     
		char c[10];
	}
	printf("%d", sizeof(Test));
	return 0;
}


如果你计算得出的答案分别为40和48,那我相信你就懂了内存对齐的奥秘了。




                
<think>好的,我需要帮助用户了解C/C++中嵌套结构体内存对齐规则和大小计算方法。首先,我应该回顾一下用户提供的引用资料,看看里面有没有关于嵌套结构体的信息。 根据引用[1]、[2]、[3],这些资料主要讨论了普通结构体内存对齐规则,包括对齐数、成员排列顺序的影响,以及为什么要内存对齐。但关于嵌套结构体,可能需要进一步扩展这些规则。 首先,基础的结构体内存对齐规则是:每个成员的偏移量必须是其类型大小和默认对齐数中较小值的整数倍。结构体的总大小必须是最大对齐数的整数倍。对于嵌套结构体,应该如何处理呢? 假设有一个结构体内部包含另一个结构体作为成员。这时候,内层结构体的对齐数应该是它自己的最大对齐数,而不是作为整体结构体的对齐数。例如,如果外层结构体有一个int和一个内嵌结构体,而内嵌结构体的最大对齐数是8,那么外层结构体的对齐数应该是max(int的对齐数,内嵌结构体的对齐数)。 接下来,我需要验证这个想法是否正确。可能需要举一个例子,比如: struct Inner { double d; // 8字节,对齐数8 char c; // 1字节,对齐数1 }; // 总大小应该是16(8+1+7填充),最大对齐数8 struct Outer { int i; // 4字节,对齐数4 struct Inner inner; // 对齐数8 short s; // 2字节,对齐数2 }; 那么计算Outer的大小时,i从0开始,占4字节。下一个成员inner的对齐数是8,所以下一个可用偏移量必须是8的倍数。i结束于3,所以inner从8开始,占用16字节,到23。然后s从24开始,占2字节,到25。此时结构体的总大小需要是最大对齐数(max(4,8,2)=8)的倍数,因此需要填充到最近的8的倍数,即32?或者计算总大小是否正确? 或者,可能我的计算有误。比如,Outer的最大对齐数应该是inner的最大对齐数8,所以总大小必须是8的倍数。i占4字节,填充4字节到8,然后inner占16字节到24,s从24开始,占2,填充到26,但总大小需要是8的倍数,所以填充到32?这样总大小是32。或者是否有其他情况? 可能需要详细步骤: 1. Outer的第一个成员i,类型int,大小4,对齐数4。起始偏移0,占0-3。 2. 第二个成员inner,类型struct Inner。它的对齐数是其内部最大对齐数,即double的8。因此,inner必须从8的倍数开始。当前偏移是4,所以需要填充4字节(4-7),然后inner从8开始,占16字节(8到23)。 3. 第三个成员s,类型short,对齐数2。当前偏移24,可以直接放置,占24-25。 现在结构体的总大小是26字节。最大对齐数是max(4,8,2)=8,因此总大小必须是8的倍数。26向上取整到32?或者可能计算错误? 或者,实际测试这个结构体的大小。可能实际结果是24?比如,inner占16字节到23,s放在24,然后总大小是24+2=26,需要填充到32?或者可能不同的编译器处理方式不同? 这可能取决于编译器的具体实现,但根据对齐规则,总大小必须是最大对齐数的整数倍,所以这里最大对齐数是8,所以总大小应该是32。但实际测试可能会更优化,比如是否存在内部填充? 可能需要考虑结构体内存对齐的具体步骤: 每个成员的起始地址必须满足其对齐要求。结构体的总大小必须是其最大成员对齐数的整数倍。在嵌套的情况下,内层结构体的最大对齐数被视为外层结构体的成员的对齐数。 所以,对于Outer结构体,其成员的对齐数分别是4(int)、8(Inner结构体的最大对齐数)、2(short)。因此,最大对齐数是8。总大小必须是8的倍数。 计算: i: 0-3 填充4-7(使inner从8开始) inner: 8-23(16字节) s: 24-25 此时总大小是26,需要填充到32(因为26不是8的倍数,下一个8的倍数是32)。因此总大小是32字节。 这可能是一个例子,但实际中是否如此呢?可能实际编译器可能会有不同的优化,但根据标准规则,应该是这样的。 接下来,需要总结嵌套结构体内存对齐规则: 1. 内层结构体作为外层结构体的成员时,其对齐数等于其自身的最大对齐数。 2. 外层结构体的总对齐数是所有成员(包括内层结构体)的最大对齐数。 3. 外层结构体的总大小必须为总对齐数的整数倍,必要时填充。 然后,用户的问题是关于嵌套结构体的规则,所以需要将这些点整理清楚,并给出示例和步骤。 另外,可能还需要考虑结构体包含数组的情况,或者多层嵌套的情况,但用户问题可能只需要基本嵌套的情况。 现在,整理回答的结构: 首先,概述嵌套结构体的对齐规则,然后详细步骤,举例子分步计算,最后给出相关的问题。 需要确保回答符合用户的要求,使用中文,行内公式用$...$,独立公式用$$...$$,但可能这里例子中的计算不需要数学公式,主要是文字和代码示例。 可能用代码示例来展示结构体的定义和大小计算过程,这样更直观。 例如: ```c struct Inner { double d; // 对齐数8 char c; // 对齐数1 }; // 大小16(8+1+7填充),对齐数8 struct Outer { int i; // 对齐数4 struct Inner s; // 对齐数8 short t; // 对齐数2 }; // 总大小计算步骤... ``` 分步计算Outer的大小: 1. i占0-3字节。 2. Inner结构体的对齐数是8,所以下一个可用偏移量是8。填充4-7(4字节)。 3. s占8-23字节(16字节)。 4. t从24开始,占24-25字节。 5. 此时总大小为26字节,需要填充到32(8的倍数)。 因此sizeof(Outer)为32。 这样详细步骤可以帮助用户理解。 同时,需要引用提供的资料,例如在解释对齐规则时引用[^1][^2][^3]。 最后,生成相关问题,如优化方法、位段的影响等。</think>### 嵌套结构体内存对齐规则详解 在C/C++中,嵌套结构体内存对齐规则是普通结构体规则的扩展,需分步骤计算。以下是核心规则和计算方法: --- #### **嵌套结构体内存对齐规则** 1. **成员对齐规则** 每个成员(包括嵌套的结构体)的起始地址必须是其**类型大小**与**编译器默认对齐数**中较小值的整数倍[^1][^3]。 2. **结构体总对齐数** 结构体的总对齐数等于其所有成员(包括嵌套结构体)的**最大对齐数**。 3. **总大小要求** 结构体的总大小必须是其总对齐数的整数倍,不足部分需填充[^3]。 --- #### **计算步骤(以示例说明)** ```c struct Inner { double d; // 大小8,对齐数8 char c; // 大小1,对齐数1 }; // 总大小:8 + 1 + 7(填充)= 16,总对齐数8 struct Outer { int i; // 大小4,对齐数4 struct Inner s; // 对齐数8(Inner的总对齐数) short t; // 大小2,对齐数2 }; ``` **分步计算 `sizeof(Outer)`** 1. **成员 `i`** - 起始偏移 `0`,占 `0-3` 字节。 2. **成员 `s`** - 对齐数 `8`,需从 `8` 的倍数开始。当前偏移为 `4`,填充 `4-7`(4字节)。 - `s` 占 `8-23` 字节(16字节)。 3. **成员 `t`** - 对齐数 `2`,当前偏移 `24`,直接放置,占 `24-25` 字节。 4. **总大小调整** - 当前总大小 `26` 字节,需填充至总对齐数 `8` 的倍数,即 `32` 字节。 最终 `sizeof(Outer) = 32`。 --- #### **关键点总结** - 嵌套结构体的对齐数等于其内部的最大对齐数。 - 外层结构体的总对齐数需重新计算,包含所有成员的对齐数。 - 填充字节可能出现在成员之间或结构体末尾。 --- #### **其他影响因素** 1. **编译器指令修改对齐数** 可使用 `#pragma pack(n)` 修改默认对齐数,但需谨慎处理嵌套结构体。 2. **位段和联合体** 嵌套结构体包含位段或联合体时,需按各自规则计算,再结合对齐原则[^1]。 ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值