目录
- 一、前言
- 二、结构体内存对齐规则
- 三、实例解析
一、前言
在讲解结构体内存对齐机制之前,我们先来看1个例子:
typedef struct
{
char sex; // 性别
int id; // 学号
char name[20]; // 姓名
float score; // 成绩
char addr[30]; // 地址
}STU;
int main(){
STU student1;
printf("%d\n",sizeof(student1));// 打印结构体变量所占内存长度
return 0;
}
以上代码示例中定义了1个结构体student1,包含5个不同数据类型的成员,分别占用内存空间:
char sex:1 Byte
int id:4 Byte
char name[20]:20 Byte
float score:4 Byte
char addr[30]:30 Byte
根据结构体特性,结构体变量所占内存长度等于其各成员所占内存长度之和
,因此这里打印的sizeof(student1)是否等于1+4+20+4+30=59呢?运行程序后结果如下:
size of student1: 64
为什么这里结构体变量student1所占内存长度是64而不是59呢?因为有结构体内存对齐机制的存在。这是很多新手程序猿很容易走进的一个误区,接下来我们一起来了解结构体对齐机制。
二、结构体内存对齐规则
- 结构体第一个成员的地址即为该结构体变量的地址。
- 结构体成员内存对齐时,其存放的内存单元大小为min{有效对齐值,指定对齐值}的最小整数倍。
注意:
自身对齐值: 结构体变量里每个成员自身所占内存大小
指定对齐值: 使用宏定义#pragma pack(N)
其中N的值即为指定对齐值。N必须是2的幂次方,例如:1,2,4,8等,如果没有使用宏定义指定对齐值,则指定对齐值取决于操作系统位数,32位操作系统默认指定对齐值为4,64位操作系统默认指定对齐值为8
。
有效对齐值: 有效对齐值为min{自身对齐值,指定对齐值}。 - 结构体总占用内存大小为min{成员最大自身对齐值,指定对齐值}的整数倍。
三、实例解析
以下示例代码均在64位Windows操作系统下运行
1、使用默认指定对齐值
使用文章前言的代码分析:
#include <stdio.h>
typedef struct
{
char sex; // 性别
int id; // 学号
char name[20]; // 姓名
float score; // 成绩
char addr[30]; // 地址
}STU;
int main(){
STU student1;
printf("size of student1: %d\n",sizeof(student1)); // 打印结构体变量所占内存长度
return 0;
}
成员 | 自身对齐值 | 指定对齐值 | 有效对齐值 |
---|---|---|---|
sex | 1 | 8 | min{1,8} |
id | 4 | 8 | min{4,8} |
name[20] | 1 | 8 | min{1,8} |
score | 4 | 8 | min{4,8} |
addr[30] | 1 | 8 | min{1,8} |
结构体总体对齐时,应按照成员自身对齐值的最大值的整数倍对齐,这里程序在没有宏定义指定默认对齐值的情况下,对齐值就应该是min{4,8},也就是4字节,这种情况下,该结构体每一个成员开辟的存储空间都为4字节。
成员 | 实际占用内存大小 | 系统开辟内存空间大小 |
---|---|---|
sex | 1 | 4 |
id | 4 | 4 |
name[20] | 20 | 20 |
score | 4 | 4 |
addr[30] | 30 | 32 |
成员id、name[20]、score实际占用内存大小刚好是对齐值4的整数倍,而成员sex则需要开辟4字节空间,实际只占用1字节,剩余3字节未使用;成员addr[30]需要开辟32字节空间,实际使用30字节,剩余2字节未使用。因此打印结构体变量student1所占内存大小为64字节。
2、修改默认指定对齐值
在上述示例基础之上修改默认指定对齐值:
#include <stdio.h>
#pragma pack(1) // 指定默认对齐值为1
typedef struct
{
char sex; // 性别
int id; // 学号
char name[20]; // 姓名
float score; // 成绩
char addr[30]; // 地址
}STU;
#pragma pack() // 取消修改对齐值
int main(){
STU student1;
printf("size of student1: %d\n",sizeof(student1)); // 打印结构体变量所占内存长度
return 0;
}
此时程序输出结果为:
size of student1: 59
以上代码使用#pragma pack(1) 将默认指定对齐值修改为1,那么结构体成员将会按照1字节做内存对齐。
成员 | 实际占用内存大小 | 系统开辟内存空间大小 |
---|---|---|
sex | 1 | 1 |
id | 4 | 4 |
name[20] | 20 | 20 |
score | 4 | 4 |
addr[30] | 30 | 30 |
此时打印结构体变量student1所占内存长度就为59。
如果将默认指定对齐值修改为2呢?
#include <stdio.h>
#pragma pack(2) // 指定默认对齐值为1
typedef struct
{
char sex; // 性别
int id; // 学号
char name[20]; // 姓名
float score; // 成绩
char addr[30]; // 地址
}STU;
#pragma pack() // 取消修改对齐值
int main(){
STU student1;
printf("size of student1: %d\n",sizeof(student1)); // 打印结构体变量所占内存长度
return 0;
}
程序输出结果:
size of student1: 60
成员 | 实际占用内存大小 | 系统开辟内存空间大小 |
---|---|---|
sex | 1 | 2 |
id | 4 | 4 |
name[20] | 20 | 20 |
score | 4 | 4 |
addr[30] | 30 | 30 |
这个时候除了成员sex之外,其余成员的自身对齐数均是2的整数倍,因此成员sex需要根据对齐数2进行内存对齐,开辟2字节空间存储,但实际只使用1字节空间,最后打印结构体变量student1的所占内存长度为60。