C语言:结构体详解

今天详解一下结构体

结构体在C语言中不同于内置类型只能解决单一对象,它更加倾向于解决有着不同信息的复杂集合体。

C语言:结构体详解_第1张图片

所以C语言添加了一种集合体描述复杂对象的集合:结构体

目录

1.结构体的声明

2.特殊的声明

3.结构体的自引用

4.结构体变量的定义和初始化

5.结构体的内存对齐

 6.修改默认对齐数

7.结构体传参


1.结构体的声明

C语言:结构体详解_第2张图片

 

一个结构体的声明需要四部分:关键字、标签、结构体成员列表、结构体变量列表。

关于结构体变量的声明大家可以在创建结构体时顺便声明,也可以在mian函数中对其声明,至于区别也就是全局变量与局部变量的区别。

C语言:结构体详解_第3张图片

当然有人会说,每次要使用就要写一遍结构体关键字和标签,会很麻烦,当然也有一种可以偷懒的办法,使用typedef对结构体重命名。

C语言:结构体详解_第4张图片

 

2.特殊的声明

在对于结构体的声明中还有一种特殊的声明,不完全声明(匿名结构体声明)

C语言:结构体详解_第5张图片

 

这种结构体声明时最好将变量声明并初始化,不然会报错,而且只能使用一个,两个不同的struct结构体按这种写法也会被认为是一个。

同时,匿名结构体与匿名结构体的指针间接等级是不同的!

3.结构体的自引用

从字面意思上看是一个结构体的对另一个结构体引用,类似函数调用。

我这里只能说,虽然类似但还是不同的。

C语言:结构体详解_第6张图片

 上面的代码虽然很规整但是在编译器上是过不去的,因为每一个结构体是独立且不是连续的空间,单纯调用是找不到的,所以一般都是采用指针的形式去编写。

C语言:结构体详解_第7张图片

结构体自引用是链表的一种使用方式。

 C语言:结构体详解_第8张图片

 链表不是连续空间存储,但是可以看出用上面示例的方法(存储一个数字+存储下一个数字地址的指针)很好解决如何在随机存储中精确找到下一个链表的数字。

C语言:结构体详解_第9张图片

所以,链表就是结构体无限套娃(笑)。

4.结构体变量的定义和初始化

变量的定义在上面提到过。可以是全局的也可以是局部的,全局变量可以跟在结构体后或者单独创建(mian函数外)

但如何在创建的同时就加上初始值呢?

C语言:结构体详解_第10张图片

 也可以在mian函数中进行变量的初始化。

5.结构体的内存对齐

结构体的内存对齐对于理解结构体是非常重要的!

结构体的内存对齐实际上就是计算一个结构体的大小,但结构体的大小是如何计算的?

#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中默认不设对齐数(对齐数是成员自身大小)

 C语言:结构体详解_第11张图片

 

 

 

 上图不难看出,int member_list是第一个成员所以从下标为0的位置往后数四个字节(灰色),而其他两个成员的类型相同所以共同占用,剩下的6和7就浪费了。

C语言:结构体详解_第12张图片

但现在我把它们的顺序调换呢?

C语言:结构体详解_第13张图片

结果不同?

那是因为在确定了一个结构体的最大对齐数(每个成员去和默认对齐数比较,其中哪个成员的字节数最大就是该结构体的最大对齐数)后,相同类型的成员挨在一起会共同占用一处空间,但调换之后只会独自占用自己的空间

所以建议书写时将相同类型成员挨在一起以达到节省空间的目的。

那可以趁热打铁的看一下tag2的大小

C语言:结构体详解_第14张图片

 C语言:结构体详解_第15张图片

 

那为什么会存在内存对齐这种方法,这在官方没有说明资料但大部分人总结出来两点:

1.平台原因

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

2.性能原因

数据结构(尤其是栈)应该尽可能的靠近自然边界对齐。

原因在于,为了访问未对其的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次。

!!简单点说就是用空间换时间

 6.修改默认对齐数

这个需要用到一个预处理指令#pragma pack

C语言:结构体详解_第16张图片

 这种方式适合结构在对齐方式不合适时,我们自己修改一个合理的默认数。

7.结构体传参

和函数的传参很相似

C语言:结构体详解_第17张图片

 这里我传的是结构体变量的地址所以访问用地址访问符(->),在传值时在结构体变量名后加上(.)即可,或者传址调用是对pa解引用然后用(.)访问。

C语言:结构体详解_第18张图片

C语言:结构体详解_第19张图片 

对了,百度有一道经典的笔试题

写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明

由于up技术力不够这里给大家贴个链接看下,毕竟结构体的内存对齐在大厂里面还是很重要的!

百度面试题——模拟实现offsetof,写一个宏,计算结构体中的成员距离首地址的偏移量是多少_一介草莽子的博客-CSDN博客

 愿大家在技术的道路上一路长虹,同时希望未来顶峰相见!

你可能感兴趣的:(c语言,开发语言)