title: C语言结构体大小分析
author: saopigqwq233
date: 2022-04-05
C语言结构体大小分析
一,基本类型
C语言自带的数据类型大小如下
数据类型 | 大小(字节) |
---|---|
char | 1 |
short | 2 |
int | 4 |
long | 4或8 |
float | 4 |
double | 8 |
long double | 16 |
二,自定义类型---struct
类似于一下形式:
struct Student{
char name[10];//学生姓名
int age;//学生年龄
};
这里自定义了一个struct Student 类型的数据类型,包含有字符数组和整形两种内容。
三,自定义结构体大小分析
1.错误示范
struct Student类型的大小应该是
10+4=14
#include"stdio.h"
#include"stdlib.h"
struct Student{
char name[10];//学生姓名
int age;//学生年龄
};
int main(
{
printf("%d\n",sizeof(struct Student;//测试struct Student大小
system("pause";
return 0;
}
运行结果
可以发现,结构体大小并非结构体成员大小简单相加
2.内存对齐
结构体成员在内存存放时,编译器会在结构体成员之间添加填充字节,以保证结构体成员的对齐要求。
2.1对齐规则
1结构体第一个成员永远放在0偏移处
可以用以下代码证明:
#include"stdio.h"
#include"stdlib.h"
struct student {
char name[20];
int age;
float score;
};
int main(
{
struct student stu;
printf("%p\n%p\n",&(stu,&(stu.name;
system("pause";
return 0;
}
运行结果如下(不同设备上运行的数值可能不相同,但是一台设备上两行的数据相同):
2)从第二个成员开始,以后的每个成员的地址距离都要对齐到某个对齐数的整数倍处,这个对齐数是:(成员自身大小和默认对齐数)的较小值
这句话是什么意思呢,让我们先看一个例子:
#include"stdio.h"
struct S
{
char a;
int b;
char c;
long long d;
}s;//创建结构体变量s
int main(
{
printf("结构体大小:%d\n",sizeof(struct S;
printf("各个成员的地址:\n";
printf("%p char a\n",&(s.a;
printf("%p int b\n",&(s.b;
printf("%p char c\n",&(s.c;
printf("%p long long d",&(s.d;
return 0;
}
运行结果如下:
因此,结构体大小:7d057(16-7d039(16=18(16=24(10
接下来我们将以excel表格代表内存空间,分析每个成员在内存的分布
其中,D列的0到23代表每个地址距离起始地址的偏移量
char a,已知结构体第一个成员永远放在0偏移处,且char 只占1字节,那么a在内存的分布暂时是这样的
int b,第二个成员要对齐到对齐数的整数倍,也就是说,它的起始地址的偏移量必须是对齐数的整数倍。
对齐数:1)数据类型自身的对齐数:char型数据自身对齐值为1字节,short型数据为2字节,int/float型为4字节,double型为8字节。
3)数据成员、结构体的有效对齐数:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。
需要注意的是gcc无默认对齐数
int b的对齐数是其自身对齐值4,如果使用的是Visual Studio这个IDE,那么其对齐数为min(4,8=4。
char a后再填补3个字节,使int b对齐,内存分布如下:
char c,其对齐数是min(1,8=1,大小是1字节,那么,直接接在偏移量为8的地方
long long d,对齐数是8,大小8字节,则需要对齐到8*2=16这个偏移量的地址,并在char c占用内存后填补7个字节,内存分布如下:
3)结构体大小是所有成员对齐数中最大对齐数的整数倍
#include"stdio.h"
struct S{
int a;
char c;
}s;
int main(
{
printf("%d struct S\n",sizeof(struct S;
printf("%p int a\n",&(s.a;
printf("%p char c\n",&(s.c;
return 0;
}
return 0;
}
接下来我们分析结构体成员是如何分布在内存中的
int a,对齐数为4,大小是4个字节,由于是第一个成员,直接放在0偏移处
char c,对齐数为1,大小是一个字节,则可以存放在偏移量为4的地方
int a和char c的对齐数分别为4和1,结构体对齐数是成员对齐数中的最大对齐数,则此结构体对齐数大小MAX(4,1=4,那么,就需要在char c后填补字节到结构体大小为8。
4)嵌套结构体中子结构体对齐到子结构体自己成员的最大对齐数的整数倍
offsetof(type, member-designator 求偏移量宏
实例可参考:C 库宏 – offsetof( | 菜鸟教程 (runoob.com
话不多说,上代码:
#include"stdio.h"
#include"stddef.h"
struct stu{
int name;
double grades;
};
struct team{
char name[6];
struct stu Students;
int num_stu;
};
int main(
{
struct team Team;
printf("%d struct stu\n",sizeof(struct stu;
printf("%d struct team\n",sizeof(struct team;
printf("%p char name[10]\n"
"%p struct stu Students\n"
"%p int num_stu\n",(Team.name,&(Team.Students,&(Team.num_stu;
printf("%d struct stu Students\n%d int num_stu\n",offsetof(struct team,Students,offsetof(struct team,num_stu;
return 0;
}
运行结果如下:
struct stu的大小是16,接下来我们看看struct team的成员是怎么分布的
char name[6],第一个成员直接占用6字节,我知道你们都懂😊
struct stu,首先,这个子结构体成员最大的对齐数是8(double,所以,需要对齐到偏移量为8的地方,符合offsetof(struct team,Students 返回值为8的结果。因此,还需要在第一个成员后补两个字节,子结构体大小为16,占用16个字节
int num_stu,占用4个字节,对齐数为4,24=4*6,所以对齐到24偏移量处。最后一个成员放进去后,结构体大小为28,不等于结构体对齐数(成员的最大对齐数)8的整数倍,所以,需要再最后一个成员后补上4个字节,让结构体大小为32=8*4。
2.2对齐数的设置
#pragma pack(n 来设置,它会影响结构体中每个成员的有效对齐值,有效对齐值是编译器的对齐数和成员大小中较小的那个。例如,如果编译器的对齐数是4,而结构体中有一个double类型的成员(占8字节),那么该成员的有效对齐值是4,而不是8。
@1 首先我们看看不带#pragma pack(n的:
#include"stdio.h"
#include"stdlib.h"
struct stu{
int age;
double grades;
};
int main(
{
printf("%d\n",sizeof(struct stu;
return 0;
}
先分析一下,int age占用四个字节,double grades对齐数为8,则需要补4个字节到int age,double grades自己占用8个字节,所以结构体大小为16个字节,看看运行结果吧:
prama pack(4 呢。
pragma pack(4
其实只是多了一个#pragma pack(4而已😏:
#pragma pack(4
#include"stdio.h"
#include"stdlib.h"
struct stu{
int age;
double grades;
};
int main(
{
printf("%d\n",sizeof(struct stu;
return 0;
}
运行结果:
int age占用4个字节,然后,double grades 对齐数=MIN(自己大小8,编译器对齐数4=4,所以不用在age后补字节,grades直接从偏移量为4的地方开始。两个成员放完后,结构体大小为12,结构体对齐数为MAX(成员们的对齐数)=4,12=4*3,则无需补字节。
感谢你阅读我的博客,如果你对我的内容有任何的意见、建议或者问题,欢迎在评论区留言,我会尽快回复。如果你发现了我的错误或者疏漏,也请不吝指正,我会及时修改。希望我的博客能对你有所帮助,也期待与你的交流和分享。