- 本文主要记述C语言中结构体类型字节占用的问题以及字节补齐的规则
一、结构体类型
C语言中的结构体struct
是一种用户自定义的数据类型,用于将不同类型的数据组合在一起。对于处理相关联的不同数据类型的数据项(例如人的姓名、年龄、住址等信息)非常有用。
二、结构体类型的大小
- 问题来了:结构体内部可以定义不同数据类型的变量,例如
typedef struct {
char name[10];
int age;
}Person;
这里定义了一个类型名为Person
的结构体,内部定义了一个字符数组用来存储姓名和一个int
类型变量用来存储年龄,那么它的大小应该如何计算呢?
char name[10]
的大小为sizeof(char)*10 = 1*10 = 10
字节int age
的大小为sizeof(int) = 4
字节
难道只是简单的将两者的大小相加10+4
就可以了吗?我们来验证一下
Person zhangsan; //创建一个结构体变量
printf("张三占用的字节数:%zu", sizeof(zhangsan)); //打印它的大小
我们可以看到变量张三的大小并不是14
而是16
,这是为什么呢?
事实上,C语言对于结构体的大小有一套字节对齐规则,有时候并不是简单地将各部分变量大小相加就可以的
1、内存对齐与结构体对齐
(1)内存对齐
内存对齐
是指将数据放置在特定的内存地址上,使得CPU能够高效地访问数据。现代计算机系统往往要求数据按照特定的字节边界进行对齐,以提高内存访问速度。例如,32位
操作系统中的int
类型通常要求以4字节对齐,即地址必须是4的倍数。
(2)结构体对齐
结构体对齐
是指结构体内部的成员按照一定的规则排列和对齐,以满足内存对齐的要求。编译器会在结构体之间和结构体尾部添加填充字节(padding
),以保证每个成员满足内存对齐要求。
2、对齐规则
-
每个成员的地址必须是该成员大小的整数倍。
例如: 4字节的int类型的地址必须是4的倍数
-
编译器会在需要时插入填充字节,以保证每个成员满足内存对齐要求
-
整个结构体的大小必须是结构体中最大的那个成员的大小的整数倍。编译器会在结构体最后插入填充字节,以保证满足对齐要求。
根据对齐要求,我们可以分析出前文中Person
类型的大小为什么是16
而不是14
了
- 假设初始地址为
0x00
-------------------------------------------------------------------------
|char|char|char|char|char|char|char|char|char|char|xxxx|xxxx|int-----int|
-------------------------------------------------------------------------
0x00|0x01|0x02|0x03|0x04|0x05|0x06|0x07|0x08|0x09|0x10|0x11|0x12...
- char name[10] 为十个字符类型的变量,每一个数组成员占一个字节
- 当字符数组存储完时,下一个地址为
0x10
,而int
型变量大小为4字节,不满足对齐规则,所以会插入两个填充字节,然后开始存储int age
- 最终结构体大小为
10+2+4=16
字节,是最大数据类型int
的整数倍,满足对齐规则,所以尾部不需要填充字节
另一个示例
struct Example2 {
char a; // 1字节
double b; // 8字节
char c; // 1字节
int d; // 4字节
};
- 假设初始地址为
0x00
-------------------------------------------------------------------------
|char|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|double|char|xxxx|xxxx|xxxx|int |
-------------------------------------------------------------------------
|0x00|0x01|0x02|0x03|0x04|0x05|0x06|0x07|0x08..|0x16|0x17|0x18|0x19|0x20|
char a
位于0x00
,占1
字节- 由于下一个是
double b
,大小为8
字节,所以需要填充7
个字节,以满足对齐条件 char c
位于0x10
,占1
字节- 下一个为
int d
,大小为4
字节,所以要填充3
个字节,以满足对齐条件 - 最后计算大小:
1 + 7 + 8 + 1 + 3 + 4 = 24
,是最大的数据类型double
的整数倍,满足对齐条件,不需要进行填充
3、自定义对齐
在C语言中,你还可以使用#pragma pack(2^n)
这个预编译指令来更改结构体的对齐方式。
- 例如
#pragma pack(1)
typedef struct
{
char name[10];
int age;
} Person;
int main()
{
Person zhangsan;
printf("张三占用的字节数:%zu", sizeof(zhangsan));
return 0;
}
这里我使用指令将字节对齐大小改为1
字节,意味着任何变量的存储地址和结构体的大小只要为1的倍数就可以
最终运行结果为
- 注意:此方法虽然可以减少结构体的内存占用,但可能会影响CPU的访问速度,需要谨慎使用
三、总结
记录此知识点以加深理解,供日后复习使用