C语言问题集——结构体及其内存对齐

C语言问题集——结构体及其内存对齐,第1张

   引入:    

            我们知道,C语言有许多内置类型,比如char、short int 、int、long int、long long int、float、double等,但是描述我们身边的信息,只有这些数据类型是远远不够的。


所以C语言有自定义类型,如数组,结构体等。


结构体:

        <1>介绍:如果我们希望描述一个人,可能就要包括TA的名字,身高,体重,年龄,家庭住址等等。


而这些信息就可以通过一个结构体变量把这些信息整合起来用来描述一个人的信息,需要用时再访问即可。


#include 
struct Student
{
	char name[20];
	double height;
	double weight;
	int age;
	char address[30];
}s1, *p;//此处的s1为全局变量,另外还创建了一个结构体指针p
//里面定义的数组和各种变量叫做该结构体的成员变量,内部不要初始化

int main()
{
	struct Student s2;//此处的s2是局部变量
	scanf("%s", s2.name);
	scanf("%lf", s2.height);

	scanf("%s", p->name);
	return 0;
}

其中 struct 是结构体关键字,后面的Student称为结构体标签,而 struct Student 合称为结构体类型,类比 int ,也叫整型类型。


        <2>结构体的访问:第一种方式:变量名+ ' . ' + 成员变量。


                                     第二种方式:结构体指针 + ' -> ' + 成员变量。


(参考上方代码)

结构体内存对齐

        初识了结构体之后,那么一个结构体变量在内存中占多大空间呢?而这个问题也就引出了结构体在内存中的存储方式的问题,而这就得聊一聊结构体内存对齐。


看下面一段代码:

#include 
struct S1
{
	char c1;
	int a;
	char c2;
};
struct S2
{
	char c1;
	char c2;
	int a;
};
int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

         可以看到,结构体S1和S2内的成员变量类型一模一样,只是位置发生了改变,而结构体的大小也发生了改变。


这是和结构体在内存中的对齐规则有关的。


对齐规则:

        1、第一个成员变量存放在与结构体偏移量为0的地址处。


        2、其它成员变量存放时,要对齐到某个数字(对齐数)的整数倍的地址处。


        3、结构体总大小为最大对齐数的整数倍(每个成员变量都有一个对齐数)。


        4、如果是嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,整个结构体的大小是所有成员中最大对齐数(含嵌套结构体的对齐数)的整数倍。


对齐数:编译器默认的一个对齐数与该成员类型大小的较小值

Visual Stdio默认为8(可修改),Linux没有默认对齐数,即成员变量类型大小就是对齐数。


注:存在数组时,该数组成员类型大小是数组内元素的类型大小。


        

例如struct S1,第一个成员变量char c1存放在偏移量为0的地址处,大小为一个byte,而第二个成员变量为int型,大小为4个byte,Visual Studio默认的对齐数为8,和int型的大小相比,4更小,所以4是当前成员的对齐数,所以存放在和它的对齐数成整数倍的地址处,如图。


放完第二个成员变量之后,再放第三个成员变量,同理,Visual Studio默认的对齐数为8,和 char 型的大小相比,1更小,所以1是当前成员的对齐数,存放在和它的对齐数成整数倍的地址处,所以紧挨着第二个成员变量存放。


        前面提到,结构体总大小是最大对齐数的整数倍,而struct S1中最大的对齐数是第二个成员变量时的4,所以虽然只用了9个byte来存储所有的成员变量,但9不是4的整数倍,所以要到12,因此struct S1的大小是12个byte。


 再看struct S2,char c1 放在偏移量为0的地址处,紧接着是第二个成员变量char c2,分析方法和Struct S1 一样,Visual Studio默认的对齐数为8,和 char 型的大小相比,1更小,所以1是当前成员的对齐数,所以放在对齐数的整数倍处,紧挨着char c1放置,最后是第三个成员变量,放在4的整数倍处,如上图,此时一共占用了8个byte,而8是4的整数倍,所以struct S2的大小就是8个字节。


为什么要有内存对齐?

 1、平台原因(移植原因)

        不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些特殊地址处读取某些特定类型的数据,否则抛出硬件异常。


2、性能原因

        数据结构(尤其是栈)应该尽可能的在自然边界上对齐,原因在于,为了访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存访问时只需要访问一次就可以拿到想要的值。


        总的来说,结构体内存对齐是以空间换取时间的做法。


那在设计结构体时,既要满足对齐,又要节省空间,要怎么做呢?

        答案是尽可能让占用空间小的变量集中到一起,就像例子中的struct S1和struct S2,将两个较小的char型变量放在一起,就起到了节省空间的作用。


水平有限,欢迎指正。


        

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/564478.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-06
下一篇 2022-04-06

发表评论

登录后才能评论

评论列表(0条)

保存