今天详解一下结构体
结构体在C语言中不同于内置类型只能解决单一对象,它更加倾向于解决有着不同信息的复杂集合体。
所以C语言添加了一种集合体描述复杂对象的集合:结构体
目录
1.结构体的声明
2.特殊的声明
3.结构体的自引用
4.结构体变量的定义和初始化
5.结构体的内存对齐
6.修改默认对齐数
7.结构体传参
一个结构体的声明需要四部分:关键字、标签、结构体成员列表、结构体变量列表。
关于结构体变量的声明大家可以在创建结构体时顺便声明,也可以在mian函数中对其声明,至于区别也就是全局变量与局部变量的区别。
当然有人会说,每次要使用就要写一遍结构体关键字和标签,会很麻烦,当然也有一种可以偷懒的办法,使用typedef对结构体重命名。
在对于结构体的声明中还有一种特殊的声明,不完全声明(匿名结构体声明)
这种结构体声明时最好将变量声明并初始化,不然会报错,而且只能使用一个,两个不同的struct结构体按这种写法也会被认为是一个。
同时,匿名结构体与匿名结构体的指针间接等级是不同的!
从字面意思上看是一个结构体的对另一个结构体引用,类似函数调用。
我这里只能说,虽然类似但还是不同的。
上面的代码虽然很规整但是在编译器上是过不去的,因为每一个结构体是独立且不是连续的空间,单纯调用是找不到的,所以一般都是采用指针的形式去编写。
结构体自引用是链表的一种使用方式。
链表不是连续空间存储,但是可以看出用上面示例的方法(存储一个数字+存储下一个数字地址的指针)很好解决如何在随机存储中精确找到下一个链表的数字。
所以,链表就是结构体无限套娃(笑)。
变量的定义在上面提到过。可以是全局的也可以是局部的,全局变量可以跟在结构体后或者单独创建(mian函数外)
但如何在创建的同时就加上初始值呢?
也可以在mian函数中进行变量的初始化。
结构体的内存对齐对于理解结构体是非常重要的!
结构体的内存对齐实际上就是计算一个结构体的大小,但结构体的大小是如何计算的?
#include
//请问结构体tag大小是几个字节?
struct tag
{
int member_list;
char name;
char id;
};
int main()
{
printf("%d", sizeof(struct tag));
return 0;
}
#include
//请问结构体tag2大小是几个字节?
struct tag
{
int member_list;
char name;
char id;
};
struct tag2
{
int aar;
struct tag;
};
int main()
{
printf("%d", sizeof(struct tag2));
return 0;
}
这里大家看的肯定是一头雾水,别急,我们一步步拆解。
结构体内存对齐的规则:
1.结构体的第一个成员直接对齐到起始位置为0的偏移处
2.从第一个成员开始,要对齐到某个对齐数的整数倍的偏移处
3.结构体的总大小必须是对齐数的整数倍,每个结构体都有一个对齐数,其中最大的对齐数就是最大对齐数
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处
对齐数:
vs编译器中默认为8,linux中默认不设对齐数(对齐数是成员自身大小)
上图不难看出,int member_list是第一个成员所以从下标为0的位置往后数四个字节(灰色),而其他两个成员的类型相同所以共同占用,剩下的6和7就浪费了。
但现在我把它们的顺序调换呢?
结果不同?
那是因为在确定了一个结构体的最大对齐数(每个成员去和默认对齐数比较,其中哪个成员的字节数最大就是该结构体的最大对齐数)后,相同类型的成员挨在一起会共同占用一处空间,但调换之后只会独自占用自己的空间
所以建议书写时将相同类型成员挨在一起以达到节省空间的目的。
那可以趁热打铁的看一下tag2的大小
那为什么会存在内存对齐这种方法,这在官方没有说明资料但大部分人总结出来两点:
1.平台原因
不是所有的硬件平台都能访问任意地址的上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.性能原因
数据结构(尤其是栈)应该尽可能的靠近自然边界对齐。
原因在于,为了访问未对其的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次。
!!简单点说就是用空间换时间
这个需要用到一个预处理指令#pragma pack
这种方式适合结构在对齐方式不合适时,我们自己修改一个合理的默认数。
和函数的传参很相似
这里我传的是结构体变量的地址所以访问用地址访问符(->),在传值时在结构体变量名后加上(.)即可,或者传址调用是对pa解引用然后用(.)访问。
对了,百度有一道经典的笔试题
写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明
由于up技术力不够这里给大家贴个链接看下,毕竟结构体的内存对齐在大厂里面还是很重要的!
百度面试题——模拟实现offsetof,写一个宏,计算结构体中的成员距离首地址的偏移量是多少_一介草莽子的博客-CSDN博客
愿大家在技术的道路上一路长虹,同时希望未来顶峰相见!