内存体结构大小讲解
·
一、结构体内存对齐:以空间换时间的设计
1. 内存对齐的核心规则
CPU 访问内存时并非逐个字节读取,而是按 “块”(如 4 字节、8 字节)读取。内存对齐通过牺牲少量内存空间,让数据存储在 CPU 高效访问的地址上,核心规则如下:
- 第一个成员从偏移量 0 的地址开始;
- 其他成员对齐到「编译器默认对齐数」和「成员自身大小」的较小值(对齐数)的整数倍地址;
- 结构体总大小为所有成员「最大对齐数」的整数倍;
- 嵌套结构体时,嵌套结构体对齐到自身最大对齐数的整数倍,整体大小为所有最大对齐数的整数倍。
注:VS 编译器默认对齐数为 8,GCC 默认对齐数为成员自身大小。
2. 实战解析:不同结构体的内存分布
示例 1:基础结构体
#include <stdio.h>
// 示例1:char + int
struct stu1 {
char a; // 偏移0,占1字节(1-3字节浪费)
int b; // 对齐数min(8,4)=4,偏移4开始,占4字节
}; // 总大小8(最大对齐数4,8是4的整数倍)
// 示例2:char + int + short
struct stu2 {
char a; // 偏移0,占1字节(1-3字节浪费)
int b; // 偏移4,占4字节
short c; // 对齐数min(8,2)=2,偏移8,占2字节(9-11字节浪费)
}; // 总大小12(最大对齐数4,12是4的整数倍)
// 示例3:优化成员顺序减少内存浪费
struct stu3 {
char a; // 偏移0,占1字节(1字节浪费)
short b; // 偏移2,占2字节
int c; // 偏移4,占4字节
}; // 总大小8(最大对齐数4,8是4的整数倍)
int main() {
printf("stu1大小:%zd\n", sizeof(struct stu1)); // 输出8
printf("stu2大小:%zd\n", sizeof(struct stu2)); // 输出12
printf("stu3大小:%zd\n", sizeof(struct stu3)); // 输出8
return 0;
}
关键结论:结构体成员顺序会影响内存占用!合理调整顺序(如将小类型成员集中)可减少内存浪费
示例 2:含数组 / 嵌套结构体的复杂场景
// 含数组的结构体
struct stu4 {
char a[11]; // 偏移0-10,占11字节(11字节浪费)
short c; // 对齐数2,偏移12,占2字节(14-15字节浪费)
double d; // 对齐数8,偏移16,占8字节
int b[2]; // 对齐数4,偏移24,占8字节
}; // 总大小32(最大对齐数8,32是8的整数倍)
// 嵌套结构体
struct stu5 {
int a[3]; // 偏移0-11,占12字节
char b[7]; // 偏移12-18,占7字节(19字节浪费)
struct stu4 c[6]; // 嵌套结构体最大对齐数8,偏移32开始,6*32=192字节
struct stu3* d; // 指针大小4/8(64位系统8),对齐数8,偏移224开始
};
二、大小端存储:数据字节的排列方式
1. 大小端的定义
- 小端存储:低字节数据存放在低地址,高字节数据存放在高地址(主流系统如 x86 架构默认)
- 大端存储:高字节数据存放在低地址,低字节数据存放在高地址(网络传输、部分嵌入式系统常用)
例如:int a = 1(十六进制0x00000001):
- 小端:内存布局
01 00 00 00 - 大端:内存布局
00 00 00 01
2. 代码实现:判断系统大小端
方法 1:指针强制类型转换
#include <stdio.h>
void Test1() {
int a = 1;
// 将int*转为char*,仅读取第一个字节
char* p = (char*)&a;
// 遍历输出4个字节(验证内存布局)
printf("内存字节布局:");
for (int i = 0; i < 4; i++) {
printf("%02x ", p[i]);
}
printf("\n");
// 判断第一个字节是否为1
if (*p == 1) {
printf("小端存储\n");
} else {
printf("大端存储\n");
}
}
方法 2:利用联合体(共用体)特性
联合体所有成员共享同一块内存,利用这一特性可简化判断:
#include <stdio.h>
union Un {
char a; // 占1字节
int b; // 占4字节(与a共享首地址)
};
void Test2() {
union Un u;
u.b = 1; // 给int赋值,仅关注首字节
if (u.a == 1) {
printf("小端存储\n");
} else {
printf("大端存储\n");
}
}
int main() {
Test1();
Test2();
return 0;
}
三、开发实战注意事项
- 结构体传参优先用指针:结构体可能占用大量内存,值传递会拷贝整个结构体,指针传递仅拷贝地址(4/8 字节),提升效率
- 内存对齐的手动调整:可通过
#pragma pack(n)修改默认对齐数(n 为对齐数),但需谨慎(可能降低 CPU 访问效率) - 跨平台开发关注大小端:网络通信、文件存储时,需统一大小端(如将数据转为大端再传输)
四、完整代码汇总
#include <stdio.h>
#include <string.h>
// 基础结构体内存对齐示例
struct stu1 {
char a;
int b;
};
struct stu2 {
char a;
int b;
short c;
};
struct stu3 {
char a;
short b;
int c;
};
// 含数组的结构体
struct stu4 {
char a[11];
short c;
double d;
int b[2];
};
// 嵌套结构体
struct stu5 {
int a[3];
char b[7];
struct stu4 c[6];
struct stu3* d;
};
// 大小端判断-方法1:指针转换
void Test1() {
int a = 1;
char* p = (char*)&a;
printf("内存字节布局:");
for (int i = 0; i < 4; i++) {
printf("%02x ", p[i]);
}
printf("\n");
if (*p == 1) {
printf("小端存储\n");
} else {
printf("大端存储\n");
}
}
// 大小端判断-方法2:联合体
union Un {
char a;
int b;
};
void Test2() {
union Un u;
u.b = 1;
if (u.a == 1) {
printf("小端存储\n");
} else {
printf("大端存储\n");
}
}
int main() {
// 输出结构体大小
printf("stu1大小:%zd\n", sizeof(struct stu1));
printf("stu2大小:%zd\n", sizeof(struct stu2));
printf("stu3大小:%zd\n", sizeof(struct stu3));
printf("stu4大小:%zd\n", sizeof(struct stu4));
// 判断大小端
printf("\n=== 大小端判断 ===\n");
Test1();
Test2();
return 0;
}
五、运行结果
stu1大小:8
stu2大小:12
stu3大小:8
stu4大小:32
=== 大小端判断 ===
内存字节布局:01 00 00 00
小端存储
总结
- 结构体内存对齐的核心是「对齐数」,总大小为最大对齐数的整数倍,合理调整成员顺序可减少内存浪费
- 小端存储是主流架构(x86)的默认方式,可通过指针强制转换或联合体快速判断
- 结构体传参优先使用指针,跨平台开发需关注大小端和内存对齐的差异
本文代码已在 VS2022(Windows)、GCC(Linux)下验证,可直接复制运行
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)