引言
结构体在C语言中虽然经常使用,但是怎么计算一个结构体占用多大的内存,很多C语言的新手都没注意过,其实C语言的语法简单,难就难在它更偏向于与底层,与内存打交道。对于嵌入式方面来说,对C语言的要求更高,因为有些硬件的内存并不像我们使用的电脑的内存那么充裕,所以需要节约内存。
结构体中同样的变量个数,却可能导致占用内存的大小不同。
例子
#include
#include
#include
int main(void)
{
// #pragma pack(8)
typedef struct a
{
int c;
double d;
char b[10];
}test1;
test1 e1;
printf("结构体 test1 占用空间大小为:%I64d\r\n", sizeof(test1));
printf("e1 的 c 的起始地址为:%p\r\n", &e1.c);
printf("e1 的 d 的起始地址为:%p\r\n", &e1.d);
printf("e1 的 b 的起始地址为:%p\r\n", &e1.b);
typedef struct b
{
double d;
int c;
char b[10];
}test2;
test2 e2;
printf("结构体 test2 占用空间大小为:%I64d\r\n", sizeof(test2));
printf("e2 的 c 的起始地址为:%p\r\n", &e2.c);
printf("e2 的 d 的起始地址为:%p\r\n", &e2.d);
printf("e2 的 b 的起始地址为:%p\r\n", &e2.b);
system("pause");
return 0;
}
输出的结果为:
结构体 test1 占用空间大小为:32
e1 的 c 的起始地址为:000000000061FE00
e1 的 d 的起始地址为:000000000061FE08
e1 的 b 的起始地址为:000000000061FE10
结构体 test2 占用空间大小为:24
e2 的 c 的起始地址为:000000000061FDE8
e2 的 d 的起始地址为:000000000061FDE0
e2 的 b 的起始地址为:000000000061FDEC
只是调换了一下int c
和double d
的位置,就导致了前者比后者多占用了8个字节的内存。这只是一个小例子。那么,结构体中到底怎么计算内存的呢?
计算方法
首先需要介绍有效对齐值:每个平台上的编译器都有默认对齐系数n,也可以通过#pragma pack(n)
来指定。
有效对齐值就等与该对齐系数和结构体中最长的数据类型的长度两者中最小的那一个值(这句话比较拗口),比如对齐系数是8,而结构体中最长的是int
(4个字节),那么有效对齐值为4。
结构体的内存计算方式遵循以下规则:
- 数据成员对齐规则:第一个数据成员放在offset为0的地方,以后的每一个成员的offset都必须是该成员的大小与有效对齐值相比较小的数值的整数倍。例子1中第一个数据成员是int 型,第二个是double,有效对齐值为8。所以double的起始地址应该为8,所以第一个int加上内存补齐用了8个字节。
- 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部有效对齐值的整数倍地址开始存储。(比如
struct a
中存有struct b
,b
里有char
,int
,double
,那b
应该从8的整数倍开始存储)。 - 收尾工作:结构体的总大小,必须是其有效对齐值的整数倍,不足的要补齐。
有了这些规则,再来看上面的两个例子。
例子1中,int
为第一个数据成员,所以下一个存储的起始位置是4,但是double
占8个字节,按规则一,不满足8的倍数,填充到16,char
一共有10个字节(数组在内存中的表示会分解成单个的),即总共26,进行收尾工作,有效对齐值为8,26不满足8的倍数,填充到32。
例子2中,double
为第一个数据成员,所以下一个存储的起始位置是8,按规则一,占用了12个字节,char
有10个字节,一共用了22个字节,进行收尾工作,该结构体中有效对齐值为8,22不满足8的倍数,填充到24。
如果手动设置#pragma pack(4)
呢
手动设置#pragma pack(4)
之后,程序输出发现test1
和test2
的大小输出是一样的,都是24。
结构体 test1 占用空间大小为:24
e1 的 c 的起始地址为:000000000061FE00
e1 的 d 的起始地址为:000000000061FE04
e1 的 b 的起始地址为:000000000061FE0C
结构体 test2 占用空间大小为:24
e2 的 c 的起始地址为:000000000061FDE8
e2 的 d 的起始地址为:000000000061FDE0
e2 的 b 的起始地址为:000000000061FDEC
补充:C语言数据类型占内存大小
数据类型 | 16位 | 32位 | 64位 |
---|---|---|---|
char 、unsigned char | 1 | 1 | 1 |
short / short int | 2 | 2 | 2 |
int、unsigned int | 2 | 4 | 4 |
long / long int | 4 | 4 | 8 |
float | 4 | 4 | 4 |
double | 8 | 8 | 8 |
long long | 8 | 8 | 8 |
long double | 8 | 12 | 16 |
指针 | 2 | 4 | 8 |