结构体中的内存管理

用户头像
Liuchengz.
关注
发布于: 2020 年 09 月 05 日
结构体中的内存管理

注:本文使用的环境为:Ubuntu18.0/64位


C语言中的结构体

结构体是C语言中的一种构造数据类型,表示一堆数据的集合,其中的成员(member)的数据类型可以不同。

字节对齐原则

为了提高CPU的访问效率,结构体的存放有着字节对齐的原则,例如在我所使用的64位操作系统环境下,CPU一次从内存中读取8个字节的数据,若数据存在对齐,则能更好的访问他们。

CPU访问内存示意图

结构体的字节对齐原则:

  • 结构体的起始位置为成员中所占内存最大成员的整数倍。

  • 各成员的起始位置,为成员自身的整数倍,若不足则在前一成员后补空。

  • 结构体的总长度为最大字节成员所占内存的整数倍,若不足则在末尾补空。

#include<stdio.h>
struct TEST
{
char a[3];
int i;
char c;
long j;
};
int main()
{
struct TEST tt;
printf("结构体的起始地址:%p\n",&tt);
printf("结构体的大小:%ld\n",sizeof(tt));
printf("a的起始地址:%p\n",&tt.a);
printf("a的大小:%ld\n",sizeof(tt.a));
printf("i的起始地址:%p\n",&tt.i);
printf("i的大小:%ld\n",sizeof(tt.i));
printf("c的起始地址:%p\n",&tt.c);
printf("c的大小:%ld\n",sizeof(tt.c));
printf("j的起始地址:%p\n",&tt.j);
printf("j的大小:%ld\n",sizeof(tt.j));
}

运行结果:

结构体的起始地址:0x7ffc9ac25f30

结构体的大小:24

a[2]的起始地址:0x7ffc9ac25f30

a[2]的大小:3

i的起始地址:0x7ffc9ac25f34

i的大小:4

c的起始地址:0x7ffc9ac25f38

c的大小:1

j的起始地址:0x7ffc9ac25f40

j的大小:8

结构体tt的内存示意图

内存优化方法

观察上面的内存示意图和代码运行结果,发现这样使用结构体使内存由大量的冗余,这时我们可以对其进行优化。

根据其字节对齐的原则我们可以有相应的优化方法:

  • 调整结构体中成员的位置

  • 使用强制对齐

  • 使用位域

1、调整结构体成员位置

根据其存放时的对齐原则,我们合理的调整结构体中成员的位置可以更大化的利用内存。

...
struct TEST
{
char a[3];
char c;
int i;
long j;
};
...

我们将char型的c前置,使后面的成员对齐时不再补空。

运行结果:

结构体的起始地址:0x7ffc247ae6c0

结构体的大小:16

a的起始地址:0x7ffc247ae6c0

a的大小:3

i的起始地址:0x7ffc247ae6c4

i的大小:4

c的起始地址:0x7ffc247ae6c3

c的大小:1

j的起始地址:0x7ffc247ae6c8

j的大小:8

优化后的内存示意图

2、强制对齐

使用#pragma pack(n)命令可改变编译器的默认对齐方式,其中n为需对齐的字节,这里我们将使用#pragma pack(1)的强制对齐方式。* 更多#pragma pack(n)的信息

...
#pragma pack(1)
...
struct TEST
{
char a[3];
int i;
char c;
long j;
};
...

运行结果:

结构体的起始地址:0x7ffc6af64e30

结构体的大小:16

a的起始地址:0x7ffc6af64e30

a的大小:3

i的起始地址:0x7ffc6af64e33

i的大小:4

c的起始地址:0x7ffc6af64e37

c的大小:1

j的起始地址:0x7ffc6af64e38

j的大小:8

优化后的内存示意图

3、使用位域

在实际使用中,对于结构体中的成员我们有时可能不需要用到它的整个内存,有可能只需要用到一个位,比如控制一个开关,我们可能只需要用到0或者1就能控制。* 更多位域信息

...
#pragma pack(1)
...
struct TEST
{
char a[3];
int i:7;
char c:1;
long j;
};
...



运行结果:

结构体的起始地址:0x7ffc9324802c

结构体的大小:12

a的起始地址:0x7ffc9324802c

a的大小:3

j的起始地址:0x7ffc93248030

j的大小:8

注意:在使用位域后不能直接查看成员的地址。

优化后内存示意图

一定需要优化吗?

观察上节中使用位域优化的例子可以发现,优化后内存占用确实是少了,但是CPU访问内存时,仍需访问两次,可见性能并没有提升,在实际应用中我们应视情况来合理的优化结构体内存的使用。比如在一组操作中,两个操作是同时进行或者使用的次数较多时,可选择将其放在同一中,以达到提升效率的目的。


参考资料:

C语言结构体丨C语言技术网

C位域丨菜鸟教程

封面:Pexels 上的 Rodolfo Clix 拍摄的图片

发布于: 2020 年 09 月 05 日 阅读数: 220
用户头像

Liuchengz.

关注

选择和时间做朋友 2018.09.18 加入

写作新人,请多批评和指正

评论

发布
暂无评论
结构体中的内存管理