C语言 结构体存储空间分配

本文详细解析了结构体内存存储空间分配原则,通过多个示例说明不同元素顺序及类型如何影响内存对齐及填充。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这两天在看结构体,一直在琢磨结构体的存储空间分配的问题,琢磨了半天总算明白了。和大家分享一下,希望能有所帮助。
相信大家都知道结构体里元素存储要对齐吧,话虽是没错,只是这个“对齐”里面包含了很多微妙的东西。首先详细的给出结构体内存存储空间分配的原则吧:编译器按照成员列表顺序一个接一个地给每个成员分配内存。只有当存储成员时需要满足正确的边界对齐要求时,成员之间才可能出现用于填充的额外内存空间。
而且还有3点要求:
结构体变量的首地址要能够被其最宽基本类型元素的大小所整除。
结构体的每个元素相对于结构体首地址的偏移量都是元素大小的整数倍,如有需要编译器会在元素之间加上填充字节。
结构体的总大小为结构体最宽基本类型元素大小的整数倍,如有需要编译器会在最末一个元素之后加上填充字节。
下面来看几个例子(32位为例):
sizeof操作符能够得出一个结构的整体长度,包括因边界对齐而跳过的那些字节。所以一般都用sizeof来计算其所占存储空间。
struct t1
{
int a;
char b;
char c;
};
首先创建的是int类型的a为4字节,但是其后一个b只占1个字节,所以不要和char b对齐。因为其后一个c只占1个字节,所以也不用对齐。所以结构体t1一共占:4+1+1=6个字节,但是根据要求3,结构体的总大小为结构体最宽基本类型元素大小的整数倍。而在这个例子中,最宽的元素为int a,为4字节。所以最后结构体t1的总大小必须为4的倍数,因此最终的字节数为8。所以,这里编译器自动充填了2字节的充填字节。
下面让我们将例1中元素的顺序调一下位置看看:
struct t2
{
char b;
int a;
char c;
};
计算后可以得到t2总字节数为12。是不是很疑惑,为什么就是位置换了一下结果会有所不同呢?这就是前面定义中所说的顺序的问题。来分析一下,首先创建的是 char b为1字节,然后是 int a为4字节,要对齐,所以应该在b后添加3个字节创建 int a,因为最后的cahr c只占1字节,所以不用充填,所以总字节数为:4+4+1=9,还是因为要求3,应该为4的倍数,所以总字节为12(3个充填字节)。
那么加上数组呢?来看看吧。
struct t3
{
int a;
char b[9];
char c;
};
分析:a占4个字节,b和c分别占9个字节和1个字节,加起来10个字节,但是因为要求3,这里最宽的元素为int,所以要为4的倍数,所以总共需要16个字节。
再掉一下位置吧.
struct t4
{
char b[9];
int a;
char c;
};
分析:b占9字节,a占4字节,为了对齐,这时候应该是12(9+3,因为char 比int少3字节,所以为了对齐要加上那3字节)+4个字节,后面又来一个c 也是char,为了对齐加4(1+3),所以总共是20个字节。而t3因为后面两个元素类型一致,所以可以先和在一起分配,然后对齐。
所以可以知道,要不要对齐是看基本元素类型的,比如虽然char b[9]比int a占的字节要多,但是因为基本元素类型不一样,所以还是要对齐。

### C语言结构体的内存分配 #### 成员顺序影响内存布局 结构体成员按照声明顺序在内存中依次分配空间。例如,在`struct S { int a; char b; float c; }`这样的定义下,编译器会先为`int`类型的成员`a`分配4字节的空间,接着为`char`类型的成员`b`分配1字节的空间,最后为`float`类型的成员`c`再分配4字节的空间[^2]。 #### 数据对齐原则 为了提高CPU访问效率,数据存储时通常遵循一定的对齐规则。这意味着某些类型的数据会被放置在特定地址边界上。具体来说: - `char` 类型占用1个字节; - `short` 类型一般占用2个字节; - `int`, `long`, 和指针类型(取决于平台)通常是4或8个字节; - 浮点数如`float`和`double`也有相应的固定长度。 当创建一个新的结构体实例时,其内部各字段之间的间距可能会被填充额外的空白位以满足这些对齐需求。这可以解释为什么有时候实际使用的总字节数大于各个单独组件之和的情况发生[^3]。 #### 嵌套结构体的影响 对于包含其他结构体作为成员的情形,整个复合对象仍然保持连续性,并且内层结构体会被视为单一整体来处理。即外层结构体中的任何子结构都将作为一个完整的单元参与外部的整体排列过程。如果存在多个不同大小的基础类型,则最终决定该组合单位尺寸的是其中最大的那个基本类型[^1]。 ```cpp #include <stdio.h> // 定义两个不同的结构体用于展示差异 struct example1 { double d; char a[5]; int b; float c; }; struct example2 { char a[4]; int d; struct example1 b; char c[10]; } w; int main() { // 输出example2结构体w的实际大小 printf("Size of 'w': %zu bytes\n", sizeof(w)); return 0; } ``` 上述代码展示了如何通过`sizeof()`运算符获取给定结构体的具体大小。这里需要注意的是,由于涉及到不同类型间的混合以及潜在的数据对齐操作,因此即使直观上看不出明显区别,实际测量得到的结果也可能超出预期。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值