0


【C语言进阶】自定义类型之结构体

文章目录


一、什么是结构体?

1. 结构体的概念

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
结构体是C语言中一种重要的数据类型,该数据类型由一组称为成员(或称为域,或称为元素)的不同数据组成,其中每个成员可以具有不同的类型。结构体通常用来表示类型不同又相关的若干数据。
结构体类型不是由系统定义好的,而是需要程序设计者自己定义的。C语言提供了关键字struct来标识所定义的结构体类型。

2. 结构体的声明

  • 图例:在这里插入图片描述
  • 举例:
structPeople{char name[20];//名字int age;//年龄char sex[5];//性别char id[30];//身份证号};//分号不能丢

3. 匿名结构体类型

在声明结构体的时候,可以不完全声明,不定义结构体类型名,这种结构体类型称为匿名结构体类型。
匿名结构也称为未命名结构,由于没有名称,因此不会创建它们的直接对象(或变量),通常我们在嵌套结构或联合中使用它们。

举例:

struct//此处省略了结构体标签(tag){int a;char b;float c;}x;//在此处创建结构体对象struct{int a;char b;float c;}a[20],*p;
提问:以上两个结构体成员变量都相同,它们的类型是否也是相同的?

在这里插入图片描述

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

  • 声明类型的同时定义变量
structStu//类型声明{char name[15];//名字int age;//年龄}s;//定义变量s
  • 声明结束后定义变量
structPoint s;//定义变量s
  • 定义变量的同时初始化
structStu s1 ={"zhangsan",19};//对变量s1初始化structStu//类型声明{char name[15];//名字int age;//年龄}s2={"lisi",20};//对变量s2初始化
  • 结构体嵌套初始化
structNode{int data;structPoint p;structNode* next;}n1 ={10,{4,5},NULL};//对变量n1初始化structNode n2 ={20,{5,6},NULL};//对变量n2初始化

二、结构体的自引用

提问:在结构体中是否可以包含一个类型为该结构体本身的成员呢?
  • 图例:在这里插入图片描述
如上图,是否可以通过链表的方式自引用找到其他成员变量呢?
  • 如若想通过链表的方式找到下一个节点,每个节点中必须要包括该节点中的数据,还要有能找到下一个节点的方式。所以,可以给每个节点都定义为一个结构体成员,那么成员变量就应该包括该节点本身的数据data和能找到下一个节点的结构体指针 struct node*。

代码示例:

structNode{int data;structNode* next;};
  • 如果觉得在定义结构体对象的时候写一长串代码很麻烦,可以利用typedef对结构体重命名

代码示例:

typedefstructNode{int data;structNode* next;}Node;//在此处写上重命名后的类型名

注意:必须在重命名之后才能使用Node(重命名后的类型名)。

三、结构体的大小

1. 结构体内存对齐

  • 结构体内存对齐规则
  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认对齐数的值为8
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

举例1:

structS1{char c1;int i;char c2;};printf("%d\n",sizeof(structS1));

运行结果:
在这里插入图片描述
分析图:
在这里插入图片描述
举例2:

structS2{char c1;structS1 s1;double d;};printf("%d\n",sizeof(structS2));

运行结果:
在这里插入图片描述
分析图:
在这里插入图片描述

2. 为什么存在内存对齐?

  1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于:为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

图例:
在这里插入图片描述

总结:结构体的内存对齐是拿空间来换取时间的做法。
因此,在设计结构体的时候,我们既要满足对齐,又要节省空间,应该让占用空间小的成员尽量集中在一起

举例:

structS3{char c1;int i;char c2;};//S3大小为12structS4{char c1;char c2;int i;};//S4大小为8

3. 修改默认对齐数

结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。

  • 更改默认对齐数
#pragmapack(1)//设置默认对齐数为1
  • 还原默认对齐数
#pragmapack()//取消设置的默认对齐数,还原为默认

四、结构体传参

  • 结构体传参
voidprint1(structS s)//直接传结构体{printf("%d\n", s.num);}
  • 结构体地址传参
voidprint2(structS* ps)//传结构体的指针{printf("%d\n", ps->num);}
提问:print1 和 print2 函数哪个好些?

首选print2函数。

  • 函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
  • 如果传递一个结构体对象的时候,则形参也需要开辟和结构体对象一样大小的空间,若结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

总结:结构体传参的时候,要传结构体的地址。



本文转载自: https://blog.csdn.net/weixin_50486535/article/details/127252355
版权归原作者 小周在忙_ 所有, 如有侵权,请联系我们删除。

“【C语言进阶】自定义类型之结构体”的评论:

还没有评论