C语言中的结构体和联合体(union)

 2024-02-05 05:01:53  阅读 0

结构名称{

会员名单

};

其中,成员列表中的每个成员都应该进行类型声明,即:

输入 name 成员名称;

比如我们需要在程序中记录一个学生()的数据,包括学号(num)、姓名(name)、性别(sex)、年龄(age)、年级(score)、地址(addr)、等等,如下图所示:

如果我们想要在图中表示数据结构,但是C语言没有提供这样现成的数据类型,所以我们需要定义一个结构体类型来表示。

结构{

整数;

字符名称[20];

炭性;

年龄;

;

字符地址[30];

};

上面定义了一个新的结构体类型(注意,它是一个键,声明结构体类型时必须使用,不能省略)。 它向编译系统声明这是一个“结构类型”,其中包括num、name、sex、age、score、addr等不同类型的数据项。

应该说,这里是一个类型名,它和系统提供的标准类型(如int、char、float等)作用相同,可以用来定义变量的类型。

结构变量

之前我们只是声明了一个结构体类型,相当于一个模型,但是里面并没有具体的数据,编译系统也没有给它分配实际的内存单元。 为了在程序中使用结构体类型数据,我们应该定义结构体类型变量并在其中存储特定的数据。 定义结构体类型变量主要有以下三种方式:

结构体类型名 结构体变量名;

比如上面我们定义了一个结构体类型,我们可以用它来声明变量:

, ;

和 类型的变量被定义,它们有一个类型结构,我们可以稍后初始化它们。

{

整数;

字符名称[20];

炭性;

年龄;

;

字符地址[30];

} , ;

其作用与第一种方法相同,即定义了两种类型的变量。 这种形式的定义的一般形式是:

结构名称{

会员名单

} 变量名列表;

{

会员名单

} 变量名列表;

关于结构类型,需要补充说明一点:

类型和变量是不同的概念,不要混淆。 我们只能对变量进行赋值、访问或操作,而不能对类型进行赋值、访问或操作。 编译时,不为类型分配空间,只为变量分配空间。

简单地说,我们可以将“结构类型”和“结构变量”理解为面向对象语言中“类”和“对象”的概念。

另外,结构体的成员也可以是结构体变量。 例如,我们首先声明一个结构体日期:

日期 {

整月;

国际日;

整数年;

};

然后将其应用到声明中:

{

整数;

字符名称[20];

炭性;

年龄;

;

日期 ;

字符地址[30];

} , ;

最后解释一下阅读大型开源代码(比如-C源码)时容易引起疑惑的一点:下面两个结构体和声明的变量在内存中实际上是一模一样的。 原因是该结构体本身没有任何额外的附加信息:

{

整数a;

整数b;

整数c;

};

{

整数a;

第1部分 {

整数b;

};

第2部分 {

整数c;

};

};

结构体变量的引用

引用结构体变量中的成员的方法是:

结构体变量名.成员名

例如,.num 表示变量中的 num 成员。 我们可以给结构体变量的成员赋值:.num = 10010;。

如果成员本身属于结构体类型,则需要使用多个成员运算符(点.)逐级查找最低级别的成员,例如:

..月= 9;

另外,还可以像普通变量一样对结构体变量的成员进行各种操作。 还可以使用地址运算符&来引用结构体变量成员的地址,或者引用结构体变量的地址。

结构变量的初始化

与其他类型变量一样,结构体变量可以在定义时指定其初始值,并用大括号括起来:

{

整数;

字符名称[20];

炭性;

年龄;

字符地址[30];

} a = {10010, "李雷", 'M', 18, " "};

结构体和数组

如果数组的元素是结构类型,则称为“结构数组”。 结构体数组与之前介绍的数值数组的区别在于,每个数组元素都是一个结构体类型数据,并且它们都包含单独的成员项。

与定义结构体变量的方法类似,只需将其声明为数组即可,例如:

{

整数;

字符名称[20];

炭性;

年龄;

;

字符地址[30];

};

斯图[3];

上面定义了一个数组stu。 该数组有3个元素,均为类型数据,如下所示:

与其他类型的数组一样,结构体数组可以被初始化,例如:

{

整数;

字符名称[20];

炭性;

年龄;

;

字符地址[30];

} Stu[3] = {{10101, "李林", 'M', 18, 87.5, ""},

{10102, "艾米", 'M', 17, 92, ""},

{10103, "宾果", 'F', 20, 100, ""}};

从上面可以看出,结构体数组初始化的一般形式是添加“={初始值表列};” 在数组定义之后。

结构体数组中的每个元素也在内存中连续存储,如下所示:

结构体和指针

结构体变量的指针是该变量所占用的内存段的起始地址。 您可以设置一个指针变量来指向结构体变量。 此时,指针变量的值就是结构体变量的起始地址。 指针变量也可用于指向结构体数组中的元素。

{

整数;

字符名称[20];

炭性;

年龄;

;

字符地址[30];

};

Stu1 = {...};

*p;

p = &stu1;

上面的代码首先声明了结构体类型,然后定义了一个类型变量stu1,同时还定义了一个指针变量p,它指向一个类型的数据。 最后将结构体变量stu1的起始地址赋值给指针变量p,如图所示:

这时可以使用*p来访问结构体变量stu1的值,使用(*p).num来访问stu的成员变量。 在C语言中为了使用起来方便直观,可以将(*p).num的定义替换为p->num,它代表p指向的结构体变量中的num成员。

换句话说,以下三种形式是等效的:

{

整数;

字符名称[20];

炭性;

年龄;

;

字符地址[30];

};

Stu[3] = {{10101, "李林", 'M', 18, 87.5, ""},

{10102, "艾米", 'M', 17, 92, ""},

{10103, "宾果", 'F', 20, 100, ""}};

*p = 斯图;

此时指针变量p指向数组第一个元素的地址,即&stu[0],也就是数组名stu。

结构体指针使用场景

(1)函数参数:使用指向结构体变量(或数组)的指针作为实参,将结构体变量(或数组)的地址传递给形参。

无效(*p);

因为如果我们直接使用结构体变量(而不是结构体指针)作为实参,由于采用“值传递”的方式,结构体变量占用的内存单元的内容都会依次传递给形参。 它们还必须是同一类型的结构变量。

函数调用过程中,形参也占用内存单元。 这种传递方式会带来较大的时间和空间开销,而且也不利于将函数执行过程中改变的形参结构的值(结果)返回给主机。 调用函数,所以一般不太可能直接“用结构体变量作为实参”,而是用指针。

(2)链表

链表是一种常见且重要的数据结构,一般用于动态存储分配。 常见的有单链表和双链表。 一般来说,可以使用结构体来表示链表节点。 以下是常见的“单链表”节点的声明:

{

整数值;

*下一个;

};

其中,val构成链表节点的值,next指针用于指向链表的下一个节点。

比如面试中经常考到的“反转单链表”的问题:

*( *头) {

如果(头== NULL){

;

if(头->下一个== NULL) {

;

* = 空;

* = 空;

* = 头;

while(!= NULL) {

* = ->下一个;

如果(==空){

= ;

->下一个=;

= ;

= ;

;

(3)二叉树

{

整数值;

*左边;

*正确的;

};

其中val表示二叉树的叶子节点的值,left指向该节点的左子树,right指向该节点的右子树。

比如之前引起轩然大波的面试题“翻转二叉树”:

*( *根) {

如果(根== NULL){

;

根->左 = (根->左);

根->右 = (根->右);

*temp = 根->左;

根->左=根->右;

根->右=临时;

;

动态分配和释放内存空间

前面提到,链表结构动态分配存储,即只有在需要时才打开节点的存储单元。 那么,如何动态打开和释放存储单元呢? C语言编译系统的库函数提供了以下相关功能。

无效*(大小);

它的作用是在内存的动态存储区域(堆)中分配一块长度为size的连续空间。 该函数的返回值是一个指向分配域起始地址的指针(类型为void *,即空指针类型。使用时可转换为其他指针数据类型)。 如果该函数执行失败(例如内存空间不足),则返回空指针NULL。

使用示例:

int * = (2 * (int));

*节点= (( ));

上面是在堆上分配的长度为2的数组。 它和int[2]的区别; 就是后者分配在内存栈区域。 而node是一个指针变量,指向一类数据的起始地址(也是分配在堆上)。

无效*(n,大小);

它的作用是在内存的动态存储区域中分配n个连续的长度为size的空间。 该函数返回一个指向分配域起始地址的指针。 如果分配不成功,则返回NULL。

无效*(无效*p,大小);

它的作用是将p指向的分配的动态内存区域的大小改为size。 大小可以大于或小于最初分配的空间。 该函数返回一个指向已分配内存区域起始地址的指针。 同样,如果分配不成功,则返回NULL。

如果传入的p为NULL,其效果与函数相同,即分配size字节的内存空间。

如果传入的size值为0,那么p指向的内存空间会被释放,但由于还没有开辟新的内存空间,所以会返回空指针NULL,类似于调用free函数。

无效自由(无效* p);

它的作用是释放p指向的内存区域,以便这部分内存区域可以被其他变量使用。 p一般是调用上述函数的返回值。 free 函数没有返回值。

工会/工会

有时,我们需要在同一个内存单元中存储几种不同类型的变量。 例如,可以将一个整型变量(2个字节)、一个字符变量(1个字节)和一个实数变量(4个字节)放在内存单元的同一起始地址处,如下图所示:

以上三个变量在内存中占用的字节数不同,但都是从同一个地址开始存储的,即几个变量相互覆盖。 这种允许多个不同变量占用同一内存的结构称为“union”类型结构,也称为“union”。

联合变量的定义

定义联合类型变量的一般形式是:

工会 工会名称 {

会员名单

} 多变的

列表; 例如:

联合数据{

整数我;

字符c;

;

} a、b、c;

也可以将类型声明与变量的定义分开:

联合数据{

整数我;

字符c;

;

};

联合数据a、b、c;

即先声明一个联合数据类型,然后将a、b、c定义为联合数据类型。 另外,还可以省略联合名称,直接定义联合变量:

联盟{

整数我;

字符c;

;

} a、b、c;

可见,“联合”和“结构”的定义形式相似,但含义不同:

引用联合变量

与结构体类似,联合体变量中成员的引用方法为:

联合变量名。 成员名字

联合变量只有先定义后才能被引用,并且不能直接引用联合变量,只能引用联合变量的成员。 例如,如果之前定义了联合变量 a,则:

但你不能只引用联合变量,例如 ("%d", a); 是错误的,因为a有多种类型的存储区域,每种类型占用不同长度的字节。 只写联合变量名a是很困难的。 允许系统确定输出哪个成员的值。

联合类型数据的特点

使用union类型数据时,应注意以下特点:

艾 = 1;

交流=“F”;

af = 2.5;

执行完以上三个赋值语句后,此时只有af有效,而ai和ac则无意义。 因此,在引用联合变量的成员时,程序员必须非常清楚联合变量中当前存储的是哪个成员。

联盟{

整数我;

字符c;

;

} a = {1, 'a', 1.5}; // 无法初始化联合体

a = 1; // 不能给联合变量赋值

m = a; // 无法引用联合变量名来获取值

社区总是让人感觉像是计算机开发早期的遗迹,当时内存空间非常宝贵。

总结

本文简要介绍C语言中结构体和联合体的概念和应用。 如有不当之处,敬请指出。

标签: 变量 结构 共用

如本站内容信息有侵犯到您的权益请联系我们删除,谢谢!!


Copyright © 2020 All Rights Reserved 京ICP5741267-1号 统计代码