概要
Struct结构体成员的存放地址以及空间大小
例如:
openAI 的 GPT 大模型的发展历程。
1、结构体成员在内存中的存放顺序
按照C99标准的内容,结构体内的成员是按照成员变量的声明顺序,依次放在内存中
例如:
#include <stdio.h>
typedef struct
{
char a;
int b;
short c;
}s;
int main()
{
s s1;
printf("s1.a = %p\n", &s1.a);
printf("s1.b = %p\n", &s1.b);
printf("s1.c = %p\n", &s1.c);
return 0;
}
那么内存中会依次存放:
字符变量a的地址
整型变量b的地址
短整型变量c的地址
打印结果如上图所示,通过地址,我们可以看到结构体中变量的存放是依次进行的
2、结构体的大小
结构体的大小是成员类型中最大类型的整数倍
例如:
`typedef struct
{
char a;
int b;
short c;
}s;
对于这个结构体来说,char型占1个字节,int类型占用4个字节,short类型占用2个字节,那么直观来看这个结构体应该总共占用1+4+2=7个字节
但是通过打印这个结构体的大小,我们可以看到运行结果:
printf("sizeof s1= %d\n", sizeof(s1));
原因有两点:
- 结构体内部的成员并不是连续紧挨着存放的
- 结构体的大小是成员类型中最大类型的整数倍
所以真正的大小是
4个字节 ✖ 3个变量 = 12个字节
3、存在这个现象的原因?
我们都知道变量的存放应该是,结构体起始地址+偏移量
那这个偏移量应该是多少呢?
我们先说结论:
成员的地址 = 结构体起始地址 + 自身类型长度 * N (N为整数)
还是以上述代码中的结构体为例
#include <stdio.h>
#include <stddef.h>
typedef struct
{
char a;
int b;
short c;
}s;
int main(void)
{
s s1;
printf("address s1 = %p \n",&s1);
printf("address s1.a = %p \n",&s1.a);
printf("address s1.b = %p \n",&s1.b);
printf("address s1.c = %p \n",&s1.c);
return 0;
}
运行结果:
结构体的起始地址为:0x7fff18e13294
成员a为char型,其前方没有其他数据,所以从结构体的起始地址开始计算,则a的地址应该是:0x7fff18e13294 + 1 * 0
成员b为int型,则地址应为:0x7fff18e13294 + 4 * 1 = 0x7fff18e13298
成员c为short型,则地址应为:0x7fff18e13294 + 2 * 4 = 0x7fff18e1329c
那应该怎么确定N呢?
首先,为了使内存的充分使用,每个成员的地址间距应该在不影响使用前提下尽可能小,所以N也尽可能小
所以这里,我们可以看到a的N取0;b的N最小也可以取0,但是取0的地址0x7fff18e13294 已经被a占用,所以取1;c的地址紧挨着b,应该为0x7fff18e13298 + 4 = 0x7fff18e1329c,这个偏移量8恰好是2的整数倍4,所以满足条件。
所以不同类型变量在结构体中的声明顺序是会对内存产生影响的
例:
typedef struct
{
char a;
int b;
short c;
}s1;
typedef struct
{
char a;
short c;
int b;
}s2;
代码片
打印地址和大小:
这里交换了short型和int型的声明顺序,可以看到内存占用少了4个字节
这是因为成员的地址是尽可能紧挨着的,所以先声明short型利用上了char型和int型之间的内存浪费。
结论:
1、结构体内的成员按照成员变量的声明顺序,依次存放在内存中;
2、结构体的大小是成员类型中最大类型的整数倍;(本质上是内存对齐,加快寻址的处理速度)
3、每个结构体成员的存放地址都等于结构体起始地址加上自身类型长度的整数倍;
4、想要减少结构体的空间占用,那么最好把数据类型相同的成员或者数据类型大小接近的成员放在一起,尽量避免不同大小的数据类型交错排列