运行之后 是8

把 char c 放后面 是12

注释char C 运行之后,还是8字节。

int a 变成 double a

加个 char C,大小变成24;

所以内存对齐的规则是,
C 语言内存对齐规则(通俗易懂版)
内存对齐是编译器 为了让 CPU 高效访问内存,自动调整结构体 / 联合体成员内存位置和占用空间 的规则,核心目的:按规则排布,让 CPU 一次就能读到数据,不用拼接。
先记住 2 个核心概念:
- 有效对齐值 :
min( 成员自身大小, 编译器默认对齐数 )- 常见默认对齐数:32 位系统默认 4 字节,64 位系统默认 8 字节
- 结构体总对齐规则 :总大小必须是最大有效对齐值的整数倍
一、基础对齐规则(最常用,背会这 4 条)
适用于结构体 struct、联合体 union:
- 第一个成员 :从偏移地址 0 开始存放
- 后续成员 :起始偏移地址 = 自身有效对齐值的整数倍
- 嵌套结构体:嵌套成员的有效对齐值 = 它内部最大成员的有效对齐值
- 整体收尾 :结构体总大小 = 最大有效对齐值的整数倍(不够就自动补空字节)
二、数据类型自身大小(基础)
表格
| 类型 | 32 位系统 | 64 位系统 |
|---|---|---|
char |
1 字节 | 1 字节 |
short |
2 字节 | 2 字节 |
int |
4 字节 | 4 字节 |
float |
4 字节 | 4 字节 |
long |
4 字节 | 8 字节 |
double |
8 字节 | 8 字节 |
指针* |
4 字节 | 8 字节 |
C 语言内存对齐实战代码 + 逐行详细计算过程
环境:32 位 GCC/VS 默认对齐数 = 4通用规则再重申
- 首成员偏移 = 0
- 成员偏移地址:必须是
min(成员自身字节数, 默认对齐数4)的整数倍 - 嵌套结构体:对齐标准 = 其内部最大基础类型字节数
- 结构体总大小:必须是整个结构体里最大对齐标准的整数倍
- 头文件依赖:
#include <stddef.h>才能用offsetof
例 1 基础结构体 S1
#include <stdio.h>
#include <stddef.h>
struct S1
{
char c1; // 1字节
short s1; // 2字节
int i1; // 4字节
};
int main(void)
{
printf("S1 大小:%zu\n", sizeof(struct S1));
printf("c1偏移:%zu\n", offsetof(struct S1, c1));
printf("s1偏移:%zu\n", offsetof(struct S1, s1));
printf("i1偏移:%zu\n", offsetof(struct S1, i1));
return 0;
}
详细计算
char c1自身 1 字节,偏移从 0 开始占用:0 号地址,占 1 字节short s1自身 2 字节,有效对齐值 = 2偏移必须是 2 的倍数,当前下一个地址是 1,不满足向后填充 1 字节,偏移定为2占用:2、3 地址int i1自身 4 字节,有效对齐值 = 4下一个地址是 4,刚好是 4 倍数,偏移4占用:4、5、6、7 地址- 整体补齐结构体最大对齐值 = 4当前总占用 8 字节,已是 4 整数倍,无需补位最终大小:8 字节
结果图

例 2 调换成员顺序 S2
#include <stdio.h>
#include <stddef.h>
struct S2
{
char c1;
int i1;
short s1;
};
int main(void)
{
printf("S2 大小:%zu\n", sizeof(struct S2));
printf("c1偏移:%zu\n", offsetof(struct S2, c1));
printf("i1偏移:%zu\n", offsetof(struct S2, i1));
printf("s1偏移:%zu\n", offsetof(struct S2, s1));
return 0;
}
详细计算
char c1:偏移 0,占 1 字节(地址 0)int i1:对齐值 4,下一个地址 1 不满足,填充 3 字节,偏移4,占 4 字节short s1:对齐值 2,下一个地址 8,满足,偏移8,占 2 字节- 现有总字节:7最大对齐值 4,向上凑 4 倍数 → 补 1 字节最终大小:12 字节
结果图

例 3 含 double 八字节类型 S3
#include <stdio.h>
#include <stddef.h>
struct S3
{
char c;
int a;
double d;
};
int main(void)
{
printf("S3 大小:%zu\n", sizeof(struct S3));
printf("c偏移:%zu\n", offsetof(struct S3, c));
printf("a偏移:%zu\n", offsetof(struct S3, a));
printf("d偏移:%zu\n", offsetof(struct S3, d));
return 0;
}
详细计算
char c:偏移 0,占 1 字节int a:对齐 4,填充 3 字节,偏移 4,占 4 字节double d:自身 8 字节,有效对齐 8下一个地址 8,满足 8 倍数,偏移8,占 8 字节- 最大对齐值 = 8总占用:13 字节,凑 8 倍数补 3 字节最终大小:16 字节
结果图

例 4 一级嵌套结构体
#include <stdio.h>
#include <stddef.h>
// 内层结构体
struct Inner
{
char ch;
int num;
};
// 外层嵌套
struct Outer1
{
char x;
struct Inner in;
double y;
};
int main(void)
{
printf("Inner大小:%zu\n", sizeof(struct Inner));
printf("Outer1大小:%zu\n", sizeof(struct Outer1));
printf("x偏移:%zu\n", offsetof(struct Outer1, x));
printf("in偏移:%zu\n", offsetof(struct Outer1, in));
printf("y偏移:%zu\n", offsetof(struct Outer1, y));
return 0;
}
第一步:先算内层 Inner
- char ch 偏移 0 占 1
- int num 对齐 4,偏移 4 占 4
- 最大对齐 4,总大小凑 4 倍数 → Inner = 8 字节Inner 对外对齐标准 = 内部最大类型 int = 4
第二步:计算外层 Outer1
- char x:偏移 0,占 1 字节
- struct Inner in:对齐标准 4下地址 1 不满足 4 倍数,填充 3 字节,偏移4,整体占 8 字节
- double y:对齐 8,下一个地址 16,偏移 16,占 8 字节
- 外层最大对齐值 = 8整体凑 8 倍数,最终 Outer1 = 24 字节
结果图

例 5 多层深度嵌套
#include <stdio.h>
#include <stddef.h>
struct A
{
char a1;
short a2;
};
struct B
{
int b1;
struct A ba;
};
struct C
{
char c1;
struct B cb;
double c2;
};
int main(void)
{
printf("A大小:%zu\n", sizeof(struct A));
printf("B大小:%zu\n", sizeof(struct B));
printf("C大小:%zu\n", sizeof(struct C));
printf("C.c1偏移:%zu\n", offsetof(struct C, c1));
printf("C.cb偏移:%zu\n", offsetof(struct C, cb));
printf("C.c2偏移:%zu\n", offsetof(struct C, c2));
return 0;
}
逐层推导
- 算 struct Aa1 (1) → a2 对齐 2,偏移 2,总大小 4,A 对外对齐值 = 2
- 算 struct Bb1 (int 4) 偏移 0 占 4ba (A) 对齐 2,直接紧跟,总大小 8,B 对外对齐值 = 4
- 算 struct Cc1 (char) 偏移 0 占 1cb (B) 对齐 4,偏移 4 开始放,占 8c2 (double) 对齐 8,偏移 16 开始放,占 8整体最大对齐 8,补齐后得出最终大小
结果图

例 6 强制 1 字节对齐
#include <stdio.h>
#include <stddef.h>
#pragma pack(1) // 强制所有成员1字节对齐,无填充
struct TestPack
{
char c;
int i;
short s;
};
#pragma pack() // 恢复系统默认对齐
int main(void)
{
printf("1字节对齐大小:%zu\n", sizeof(struct TestPack));
return 0;
}
计算
取消所有填充,直接顺序排布大小 = 1+4+2 = 7 字节,无任何空位填充。
结果图

做题固定流程(直接套用)
- 逐个确定每个成员有效对齐数
- 从前往后依次定偏移地址,不够就补空字节
- 嵌套结构体先算内层大小 + 内层最大对齐值
- 全部排完后,用全局最大对齐值补齐整个结构体总长度