您现在的位置是:首页 >技术交流 >嵌入式 C/C++ 开发之压榨程序大小——结构体优化:用好了能省 50% 的空间网站首页技术交流
嵌入式 C/C++ 开发之压榨程序大小——结构体优化:用好了能省 50% 的空间
嵌入式 C/C++ 开发之压榨程序大小——结构体优化:用好了能省 50% 的空间
在 C
和 C++
编程中,结构体(struct
)的内存布局对程序的性能和内存使用有着重要的影响,尤其是在嵌入式开发中,RAM
也就那么 16K
、8K
甚至 4K
,寸土寸金,容不得一丝浪费。本文将详细介绍如何通过理解对齐要求、填充、结构体重新排序等技术来优化结构体的内存布局,缩小程序占用空间,从而提高程序的效率和性能。
1. 对齐要求
编译器在内存中布局基本数据类型时受到对齐要求的约束,以加快内存访问速度。这些约束导致了相同的布局,包括 Intel
、ARM
和 RISC-V
等架构。除了 char
类型外,其他基本的 C
语言数据类型都有对齐要求。例如,2
字节的 short
必须从偶数地址开始,4
字节的 int
或 float
必须从能被 4
整除的地址开始,8
字节的 long
或 double
必须从能被 8
整除的地址开始。这种自我对齐的特性使得内存访问更快。
但是,这也导致了部分内存空间的额外占用——填充。
2. 填充
填充是指编译器在结构体成员之间或结构体末尾添加的额外字节,以满足对齐要求。例如,考虑以下结构体(64
位系统下):
struct foo1 {
char *p; /* 8 字节 */
char c; /* 1 字节 */
long x; /* 8 字节 */
};
在这个结构体中,char
类型的 c
成员后面需要添加 7
字节的填充,以确保 long
类型的 x
成员从 8
字节对齐的地址开始:
struct foo1 {
char *p; /* 8 字节 */
char c; /* 1 字节 */
//char pad[7]; /* 7 字节填充 */
long x; /* 8 字节 */
};
这种填充虽然可以确保数据的快速访问,但也导致内存的浪费。
3. 结构体对齐和填充
结构体的对齐通常是按照其成员最宽的长度进行对齐。例如,如果一个结构体包含一个 long
类型的成员,那么整个结构体将具有 8
字节对齐。这意味着结构体的地址与其第一个成员的地址相同,没有前面的填充。然而,结构体的末尾可能会有尾随填充,以确保下一个结构体实例从正确的对齐地址开始。
4. 位字段
位字段允许你在结构体中声明小于字符宽度的字段,小到 1
位。例如:
struct foobits {
short s;
char c;
int flip:1;
int nybble:4;
int septet:7;
};
位字段的布局受到 C
标准的限制,它们不能跨越存储单位边界。C99
保证位字段将尽可能紧密地打包,但 C++14
放宽了这一限制,允许位字段跨越多个分配单元。
5. 结构体重新排序
由上述对其及填充的规则学习得知,我们可以通过重新排序结构体成员,来减少填充并优化内存布局。
通常,按对齐要求从大到小的顺序排列成员可以减少填充。
例如,将指针对齐的成员放在前面,然后是 4
字节的 int
,接着是 2
字节的 short
,最后是 char
类型的成员。
这种方法可以显著减少结构体的总体大小,从而提高内存使用效率。
6. 结构体优化的实际应用
考虑一个复杂的结构体,包含多种不同类型的数据成员:
struct complex {
char c1;
int i1;
short s1;
long l1;
char c2;
};
在这个结构体中,c1
和 c2
之间以及 l1
末尾会有填充。通过重新排序成员,我们可以减少填充:
struct complex {
long l1;
int i1;
short s1;
char c1;
char c2;
};
这种重新排序后的结构体只在 l1
和 i1
之间有 4 字节的填充,因为 i1
需要 4
字节对齐。c1
和 c2
之间没有填充,因为 c1
已经在 i1
的填充内。
优化对比,文末可获取 demo
:
结论
理解并应用结构体优化技术对于提高 C
和 C++
程序的性能和内存效率至关重要。
通过对齐要求、填充和结构体重新排序的理解,开发者可以设计出更高效、更紧凑的数据结构,从而在保持代码可读性的同时,提高程序的整体性能。
记住,优化结构体不仅仅是减少内存占用,它还可以提高程序的运行速度,特别是在处理大量数据或在内存受限的环境中。
文章配套 demo
源码仓库:结构体优化对比demo
也可扫码关注博主同名公众号"不解之榬",私信获取