关于结构体所占字节数,内存对齐心得

结构体对齐与填充
本文解析了在VC++6.0环境下结构体成员变量如何根据对齐规则进行内存分配和填充,解释了不同成员顺序导致结构体大小的变化。

【备注】:在VC++6.0平台,32位系统上运行测试
先看一个例子:
【A】struct student
{
    int         num;  
    char      name;
    double  mark;
    

【B】struct student
{
   int         num;
   double  mark;  
   char      name;

也许大家一看这A,B两个结构体不是一样的么想想这个结构体占的字节数?“也许”大家都会以为 A和B结构体所占的字节数都是:sizeof(struct student)=sizeof(char)+sizeof(int)+sizeof(double)=1+4+8=13  个字节,貌似看起来没有问题,但是实际上呢!!!
所占字节数: 【A】:  16个字节    【B】: 24个字节
怎么会这样!!!!
【为什么会这样】:为了提高CPU的存储速度,VC对一些变量的起始地址做了“对齐”处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。
即:
char   偏移量必须为sizeof( char )即1的倍数   

int    偏移量必须为sizeof( int  )即4的倍数
double 偏移量必须为sizeof(double)即8的倍数
float  偏移量必须为sizeof( float)即4的倍数
 

各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充,填充部分没有放任何有意义的东西,结构体的总字节大小必须是该结构中占用最大空间的类型所占用的字节数的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。

先看结构体【A】:int num//偏移量为0,满足对齐方式,num占用4个字节;char name //因为前面的num占用4个字节,这时分配的地址对于结构的起始地址的偏移量为4,是sizeof(char)=1的4倍,满足对齐方式,所以char   占用sizeof(char)=1个字节数目,此时给下一个可以分配的地址对于结构体起始地址的偏移量为4+1=5,此时不是下一个sizeof(double)=8的倍数所以不满足对齐方式,所以VC自动填充3个字节(这三个字节没有放什么东西),这时下一个可以分配的地址对于结构的起始地址的偏移量为8,刚好是sizeof(double)=8的倍数,所以把存放 mark 在偏移量为8的地方,该成员变量占用sizeof(double)=8个字节;这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:4+1+3+8=16,刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以没有空缺的字节需要填充。所以整个结构的大小为:sizeof(struct student)=4+1+3+8=16!********同理可得结构体【B】所占内存的字节数是  24~

运行结果如下图所示:

【总结】: 结构体大小不一定是内部变量大小的和!!!!!!!

转载于:https://www.cnblogs.com/BorisFong/p/6112734.html

<think>我们被要求计算C语言结构体字节数。根据引用内容,结构体的大小不仅取决于各成员的大小,还受到内存对齐规则的影响。内存对齐是为了提高CPU访问内存的效率。下面详细介绍计算步骤: 1. **基本概念**: - 结构体的每个成员相对于结构体首地址的偏移量(offset)必须是该成员类型大小与编译器默认对齐数(alignment)中较小值的整数倍。 - 结构体的总大小必须是其所有成员中最大对齐数(max alignment)的整数倍。 2. **对齐规则**(以32位系统为例,通常默认对齐数为4字节;64位系统通常为8字节,但具体取决于编译器): - **规则1**:第一个成员的偏移量为0,即从结构体首地址开始。 - **规则2**:其他成员的偏移量 = min(该成员类型大小, 默认对齐数) 的整数倍。如果不是,则编译器会在前面填充字节(padding)以满足要求。 - **规则3**:结构体的总大小必须是所有成员中最大对齐数(即每个成员的类型大小与默认对齐数取最小后的最大值)的整数倍。如果不是,则在最后一个成员后填充字节。 3. **计算步骤**: - **步骤1**:确定每个成员的对齐数(alignment) = min(成员类型大小, 默认对齐数)。例如,int类型在32位系统中大小为4字节,默认对齐数通常为4,所以对齐数为4。 - **步骤2**:按顺序计算每个成员的偏移量和填充: - 第一个成员偏移量为0。 - 后续成员偏移量必须是对齐数的整数倍,如果不是,则填充至最近的倍数。 - 每个成员用其类型大小的空间。 - **步骤3**:计算完所有成员后,检查当前总大小(即最后一个成员的偏移量+大小)是否等于最大对齐数的整数倍,如果不是,则填充至最近的倍数。 4. **示例1**(引用[4]中的例子): ```c struct u1 { char x; // 1字节 int y; // 4字节 char z; // 1字节 }; ``` 假设默认对齐数为4(32位系统): - 成员x(char):对齐数=min(1,4)=1,偏移量0,用1字节。 - 成员y(int):对齐数=min(4,4)=4,要求偏移量是4的倍数。当前偏移量是1,需要填充3字节(达到偏移量4),然后y用4字节(偏移量4~7)。 - 成员z(char):对齐数=1,偏移量8(7+1=8,是1的倍数),用1字节(偏移量8)。 - 当前总大小=9字节。最大对齐数为4,9不是4的倍数,所以填充3字节(达到12)。因此总大小为12字节。 5. **示例2**(引用[2]中的例子,结构体嵌套): ```c struct S3 { double d; // 8字节 char c; // 1字节 int i; // 4字节 }; // 假设默认对齐数为8(64位系统) // 计算S3: // d: 对齐数=min(8,8)=8,偏移0~7。 // c: 对齐数=1,偏移8(是1的倍数),用1字节(偏移8)。 // i: 对齐数=4,要求偏移是4的倍数。当前偏移9,需填充3字节(达到12),然后i用4字节(12~15)。 // 总大小=16字节(16是8的倍数)。 // 所以S3大小为16字节。 struct S4 { char c1; // 1字节 struct S3 s; // 16字节(嵌套结构体) double d; // 8字节 }; ``` 计算S4(64位系统,默认对齐数8): - 成员c1(char):对齐数=1,偏移0,用1字节。 - 成员s(struct S3):嵌套结构体,其对齐数取S3的最大对齐数(即8)。所以要求偏移量是8的倍数。当前偏移量是1,需要填充7字节(达到8),然后s用16字节(偏移8~23)。 - 成员d(double):对齐数=8,当前偏移24(是8的倍数),用8字节(偏移24~31)。 - 总大小=32字节(32是8的倍数)。所以S4大小为32字节。 6. **注意事项**: - 默认对齐数可以通过`#pragma pack(n)`改变(n=1,2,4,8等),此时对齐数计算变为min(成员类型大小, n)。 - 结构体嵌套时,内层结构体对齐数为其成员中的最大对齐数(而不是总大小)。 - 数组作为成员时,按单个元素类型计算对齐数(如`char a[5]`,对齐数为1)。 7. **总结公式**: 结构体大小 = 最后一个成员的偏移量 + 最后一个成员的大小 + 尾部填充(使总大小是最大对齐数的整数倍) 根据上述规则,我们可以计算任意结构体的大小。在编程中,也可以使用`sizeof`运算符直接获取,但理解计算原理对内存优化至关重要[^1][^2][^4][^5]。</think>### C语言结构体字节数计算方法 在C语言中,结构体的大小由成员大小、排列顺序和**内存对齐规则**共同决定。以下是详细的计算方法和步骤: --- #### 一、内存对齐的基本原则 1. **起始位置规则** 结构体一个成员的偏移量(offset)始终为0[^4]。 2. **成员对齐规则** 每个成员相对于结构体首地址的偏移量必须是 **min(成员大小, 默认对齐数)** 的整数倍[^4]。 - 默认对齐数通常是系统字长(32位系统为4字节,64位系统为8字节) - 可通过 `#pragma pack(n)` 修改(n=1/2/4/8/16) 3. **整体对齐规则** 结构体总大小必须是 **所有成员对齐数中最大值** 的整数倍[^1]。 --- #### 二、计算步骤(示例分析) ##### 示例1:基础结构体 ```c struct S1 { char a; // 1字节 int b; // 4字节 short c; // 2字节 }; ``` **计算过程(假设默认对齐数=4)**: 1. `a` 偏移量=0(规则1),用1字节 → 位置 [0] 2. `b` 需对齐到 min(4,4)=4 的倍数 - 当前偏移量=1 → 填充3字节([1][2][3]) - `b` 用4字节 → 位置 [4-7] 3. `c` 需对齐到 min(2,4)=2 的倍数 - 当前偏移量=8(是2的倍数),用2字节 → 位置 [8-9] 4. 总大小=10字节 - 需满足整体对齐(最大对齐数=4) - 10不是4的倍数 → 末尾填充2字节 → **总大小=12字节** --- ##### 示例2:嵌套结构体(引用[2]案例) ```c struct S3 { double d; // 8字节 char c; // 1字节 int i; // 4字节 }; // 大小=16字节(计算过程略) struct S4 { char c1; // 1字节 struct S3 s3; // 16字节(嵌套) double d; // 8字节 }; ``` **计算过程(64位系统,默认对齐数=8)**: 1. `c1` 偏移量=0 → 位置 [0] 2. `s3` 需对齐到其最大对齐数(double为8) - 当前偏移量=1 → 填充7字节([1]-[7]) - `s3` 用16字节 → 位置 [8-23] 3. `d` 需对齐到 min(8,8)=8 的倍数 - 当前偏移量=24(是8的倍数)→ 位置 [24-31] 4. 总大小=32字节(32是8的倍数)→ **无需填充** --- #### 三、关键注意事项 1. **数据类型大小**(常见): ```c char // 1字节 short // 2字节 int // 4字节 float // 4字节 double // 8字节 pointer // 4字节(32位)/8字节(64位) ``` 2. **优化技巧**: - 按成员大小**降序排列**可减少填充(如 `double→int→char`) - 避免小成员夹在大成员之间 3. **验证方法**: ```c printf("Size: %zu\n", sizeof(struct YourStruct)); ``` --- #### 四、特殊场景 1. **空结构体**:C11标准规定大小至少为1字节 2. **位域成员**:按声明顺序压缩存储,但受对齐规则约束 3. **柔性数组成员**:不参与sizeof计算 > 内存对齐牺牲部分空间换取CPU访问效率,避免多次内存读取(如未对齐的int可能需读2次)[^5]。实际开发中可用 `offsetof` 宏验证成员偏移量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值