设为首页 加入收藏

TOP

C 语言中的结构体和共用体(联合体)(一)
2019-01-03 20:09:08 】 浏览:227
Tags:言中 结构 共用 联合体

本文主要总结了谭浩强主编的《C 程序设计》教材中结构体和共用体相关章节的内容。


在 C 语言中, 结构体(struct) 是一个或多个变量的集合,这些变量可能为不同的类型,为了处理的方便而将这些变量组织在一个名字之下。由于结构体将一组相关变量看作一个单元而不是各自独立的实体,因此结构体有助于组织复杂的数据,特别是在大型的程序中。


共用体(union),也称为 联合体 ,是用于(在不同时刻)保存不同类型和长度的变量,它提供了一种方式,以在单块存储区中管理不同类型的数据。


今天,我们来介绍一下 C 语言中结构体和共用体的相关概念和使用。


声明一个结构体类型的一般形式为:


其中,成员列表中对各成员都应进行类型声明,即:


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



如果要表示图中的数据结构,但 C 语言并没有提供这种现成的数据类型,因此我们需要用定义一种结构体类型来表示。


上述定义了一个新的结构体类型 struct student (注意, struct 是声明结构体类型时所必须使用的关键及,不能省略),它向编译系统声明,这是一个“结构体类型”,它包括 numnamesexagescoreaddr 等不同类型的数据项。


应当说,这里的 struct student 是一个类型名,它与系统提供的标准类型(如 intcharfloatdouble 等)具有同样的作用,都可以用来定义变量的类型。


前面只是声明了一个结构体类型,它相当于一个模型,但其中并无具体的数据,编译系统对其也不分配实际的内存单元。为了能在程序中使用结构体类型的数据,我们应当定义结构体类型的变量,并在其中存放具体的数据。主要以下 3 中方式定义结构体类型变量:


例如上面我们已经定义了一个结构体类型 struct student ,就可以用它来声明变量:


定义了 student1student2struct student 类型的变量,它们具有 struct student 类型的结构,后续我们可以对它们进行初始化。


例如:


它的作用与第一种方法相同,即定义了两个 struct student 类型的变量 student1student2 。这种形式的定义的一般形式为:


其省略了结构体名,一般形式为:


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


类型与变量是不同的概念,不要混淆。我们只能对变量赋值、存取或运算,而不能对一个类型进行赋值、存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间。


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


此外,结构体里的成员也可以是一个结构体变量。比如我们先声明了一个结构体 struct date


然后把它应用于声明 struct student 中:


最后,解释一个在阅读大型开源代码(比如 Objective-C Runtime 源码)时容易产生疑问的点:如下两个结构体 SampleASampleB 声明的变量在内存上其实是完全一样的,原因是因为结构体本身并不带有任何额外的附加信息:


引用结构体变量中成员的方式为:


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


如果成员本身又属于一个结构体类型,则要用若干个成员运算符(点号 . ),一级一级地找到最低一级的成员,例如:


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


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


如果一个数组的元素为结构体类型,则称其为“结构体数组”。结构体数组与之前介绍的数值型数组的不同之处在于每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员项。


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


以上定义了一个??组 stu ,数组有 3 个元素,均为 struct student 类型数据,如下图:



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


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


结构体数组中各元素在内存中也是连续存放的,如下图:



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


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



根据上一篇文章的介绍,此时可以用 *p 来访问结构体变量 stu1 的值,用 (*p).num 来访问 stu 的成员变量。C 语言为了使用方便和直观,定义可以把 (*p).num 改用 p->num 来代替,它表示 p 所指向的结构体变量中的 num 成员。


也就是说,以下 3 种形式 等价


(1)结构体变量.成员名: stu1.num
(2)( 指针变量名).成员名:`( p).num (3)指针变量名->成员名: p->num`


在上一篇文章中,已经介绍了可以使用指向数组或数组元素的指针,同样地,对于结构体数组及其元素也可以用指针变量来指向,例如:


此时,指针变量 p 指向数组首个元素的地址,即 &stu[0] ,也就是数组名 stu ,具体的细节上篇文章已经介绍过,我们这里不再赘述。


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


因为如果我们直接用结构体变量(不是结构体指针)作为实参时,由于采取的是“值传递”的方式,将结构体变量所占用的内存单元的内容全部顺序传递给形参,形参也必须是同类型的结构体变量。在函数调用期间,形参也要占用内存单元,这种传递方式将带来较大的时间和空间开销,同时也不利于将在函数执行期间改变形参结构体的值(结果)返回给主调函数,因此一般比较少直接“用结构体变量做实参”,而是改用指针的形式。


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


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


例如,面试比较常考察的“反转单链表”的题目:


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


例如,之前闹得沸沸扬扬的 Google 面试“翻转二叉树”的题目:


前面介绍,链表结构是动态地分配存储的,即在需要时才开辟一个节点的存储单元。那么,怎样动态地开辟和释放存储单元呢?C 语言编译系统的库函数提供了以下相关函数。


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


使用示例:


上述 result 是一个分

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C++虚函数在g++中的实现分析 下一篇C/C++虚函数实现的基本原理

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目