肯尼斯·里科《C和指针》第10章 结构和联合(1)结构的基础知识

news2024/11/17 5:50:42

聚合数据类型(aggregate data type)能够同时存储一个以上的单独数据。C提供了两种类型的聚合数据类型:数组和结构

数组是相同类型的元素的集合,它的每个元素是通过下标引用或指针间接访问来选择的。

结构也是一些值的集合,这些值称为它的成员(member),但一个结构的各个成员可能具有不同的类型。结构和Pascal或Modula中的记录(record)非常相似。

数组元素可以通过下标访问,这只是因为数组的元素长度相同。但是,在结构中情况并非如此。由于一个结构的成员可能长度不同,因此不能使用下标来访问它们。相反,每个结构成员都有自己的名字,它们是通过名字访问的。

这个区别非常重要。结构并不是一个它自身成员的数组。和数组名不同,当一个结构变量在表达式中使用时,它并不被替换成一个指针。结构变量也无法使用下标来选择特定的成员。

结构变量属于标量类型,所以可以像对待其他标量类型那样执行相同类型的操作。结构也可以作为传递给函数的参数,它们也可以作为返回值从函数返回;相同类型的结构变量相互之间可以赋值。可以声明指向结构的指针,取一个结构变量的地址,也可以声明结构数组。但是,在讨论这些话题之前,我们必须知道一些更为基础的东西。

结构声明

在声明结构时,必须列出它包含的所有成员。这个列表包括每个成员的类型和名字。

struct tag { member-list } variable-list ;

这里有几个例子。

这个声明创建了一个名叫x的变量,它包含3个成员:一个整数、一个字符和一个浮点数。

这个声明创建了y和z。y是一个数组,它包含了20个结构。z是一个指针,它指向这个类型的结构。

这两个声明被编译器当作两种截然不同的类型,即使它们的成员列表完全相同。因此,变量y和z的类型与x的类型不同,所以下面这条语句是非法的。

z = &x;

这是不是意味着某种特定类型的所有结构都必须使用一个单独的声明来创建呢?

幸运的是,事实并非如此。标签(tag)字段允许为成员列表提供一个名字,这样它就可以在后续的声明中使用。标签允许多个声明使用同一个成员列表,并且创建同一种类型的结构。这里有个例子:

这个声明把标签SIMPLE和这个成员列表联系在一起。该声明并没有提供变量列表,所以它并未创建任何变量。

这个声明类似于制造一个甜饼切割器。甜饼切割器决定了做出来的甜饼的形状,但其本身却不是甜饼。标签标识了一种模式,用于声明未来的变量,但无论是标签还是模式,其本身都不是变量

struct  SIMPLE  x;
struct  SIMPLE  y[20], *z;

这些声明使用标签来创建变量。它们创建和最初两个例子一样的变量,但存在一个重要的区别——现在x、y和z都是同一种类型的结构变量。

声明结构时可以使用的另一种良好技巧是用typedef创建一种新的类型,如下面的例子所示:

这个技巧和声明一个结构标签的效果几乎相同。区别在于Simple现在是个类型名而不是个结构标签,所以后续的声明可能像下面这个样子:

Simple  x;
Simple  y[20], *z;

如果想在多个源文件中使用同一种类型的结构,就应该把标签声明或typedef形式的声明放在一个头文件中。如果源文件需要这个声明,可以使用#include指令把那个头文件包含进来。

批注:比上课讲的会更好理解些吧。

结构成员

到目前为止的例子里,只使用了简单类型的结构成员,但可以在一个结构外部声明的任何变量都可以作为结构的成员。尤其是,结构成员可以是标量、数组、指针甚至是其他结构。

这里有一个更为复杂的例子:

一个结构的成员的名字可以和其他结构的成员的名字相同,所以这个结构的成员a并不会与struct SIMPLE s的成员a冲突。正如接下去看到的那样,成员的访问方式允许指定任何一个成员而不至于产生歧义。

结构成员的直接访问

结构变量的成员是通过点操作符(.)访问的。点操作符接受两个操作数:左操作数就是结构变量的名字;右操作数就是需要访问的成员的名字。这个表达式的结果就是指定的成员。例如,考虑下面这个声明:

struct COMPLEX comp;

名字为a的成员是一个数组,所以表达式comp.a就选择了这个成员。这个表达式的结果是个数组名,所以可以把它用在任何可以使用数组名的地方。类似地,成员s是个结构,所以表达式comp.s的结果是个结构名,它可以用于任何可以使用普通结构变量的地方。尤其是,我们可以把这个表达式用作另一个点操作符的左操作符,如(comp.s).a,用来选择结构comp的成员s(也是一个结构)的成员a。点操作符的结合性是从左向右,所以可以省略括号,表达式comp.s.a表示同样的意思。

这里有一个更为复杂的例子。成员sa是一个结构数组,所以comp.sa是一个数组名,它的值是一个指针常量。对这个表达式使用下标引用操作,如(comp.sa)[4]将选择一个数组元素。但这个元素本身是一个结构,所以可以使用另一个点操作符取得它的成员之一。下面就是一个这样的表达式:

( (comp.sa)[4] ).c

下标引用和点操作符具有相同的优先级,它们的结合性都是从左向右,所以可以省略所有的括号。下面的表达式和前面那个表达式是等效的。

comp.sa[4].c

结构成员的间接访问

如果你拥有一个指向结构的指针,该如何访问这个结构的成员呢?首先就是对指针执行间接访问操作,从而获得这个结构。然后再使用点操作符来访问它的成员。但是,点操作符的优先级高于间接访问操作符,所以必须在表达式中使用括号,确保间接访问首先执行。举个例子,假定一个函数的参数是个指向结构的指针,如下面的原型所示:

void    func( struct COMPLEX *cp );

函数可以使用下面这个表达式来访问这个变量所指向的结构的成员f:

(*cp).f

对指针执行间接访问将访问结构,然后点操作符访问一个成员。

由于这个概念有点惹人厌,因此C语言提供了一个更为方便的操作符来完成这项工作——->操作符(也称箭头操作符)。和点操作符一样,箭头操作符接受两个操作数,但左操作数必须是一个指向结构的指针。箭头操作符对左操作数执行间接访问取得指针所指向的结构,然后和点操作符一样,根据右操作数选择一个指定的结构成员。但是,间接访问操作内建于箭头操作符中,所以不需要显式地执行间接访问或使用括号。这里有一些例子,它们像前面一样使用同一个指针。

cp->f
cp->a
cp->s

第1个表达式访问结构的浮点数成员,第2个表达式访问一个数组名,第3个表达式则访问一个结构。后文将给出为数众多的例子,可以帮助大家弄清如何访问结构成员。

结构的自引用

在一个结构内部包含一个类型为该结构本身的成员是否合法呢?这里有一个例子,可以说明这个想法:

这种类型的自引用是非法的,因为成员b是另一个完整的结构,其内部还将包含它自己的成员b。这第二个成员又是另一个完整的结构,它还将包括它自己的成员b。这样重复下去永无止境。这有点像永远不会终止的递归程序。但下面这个声明却是合法的,你能看出其中的区别吗?

这个声明和前面那个声明的区别在于b现在是一个指针而不是结构。编译器在结构的长度确定之前就已经知道指针的长度,所以这种类型的自引用是合法的。

如果你觉得一个结构内部包含一个指向该结构本身的指针有些奇怪,请记住它事实上所指向的是同一种类型的不同结构。更加高级的数据结构,如链表和树,都是用这种技巧实现的。每个结构指向链表的下一个元素或树的下一个分枝。

结构的初始化

结构的初始化方式和数组的初始化很相似。一个位于一对花括号内部、由逗号分隔的初始值列表可用于结构中各个成员的初始化。这些值根据结构成员列表的顺序写出。如果初始列表的值不够,剩余的结构成员将使用缺省值进行初始化。

结构中如果包含数组或结构成员,其初始化方式类似于多维数组的初始化。一个完整的聚合类型成员的初始值列表可以嵌套于结构的初始值列表内部。这里有一个例子:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1435727.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EasyX图形库学习(三、用easyX控制图形界面中的小球、图片-加载、输出)

目录 小球视频 图像输出函数 loadimage用于从文件中读取图片 putimage在当前设备上绘制指定图像。 initgraph 函数 图片输出 代码详解: 1. 初始化图形界面 2. 设置背景颜色并清除屏幕 3. 加载并显示图片 4. 等待用户输入并退出程序 图形界面中的小球 1…

AI新工具(20240205) AI 对联/春联 - 输入描述,自动生成春联图片;AI写作引擎;满分简历

AI 对联/春联 - 输入描述,自动生成春联图片 AI 对联/春联 AI 对联/春联是一个由YunYouJun开发的开源项目,用于生成春节对联。 https://github.com/YunYouJun/ai-sfc 满分简历 - HR在7秒内决定一份简历去留,让你的简历脱颖而出 满分简历 …

服务器和CDN推荐

简介 陆云Roovps是一家成立于2021年的主机服务商,主要业务是销售美国服务器、香港服务器及国外湖北十堰高防服务器,还有相关CDN产品。( 地址:roovps) 一、相关产品

计算机设计大赛 深度学习+opencv+python实现昆虫识别 -图像识别 昆虫识别

文章目录 0 前言1 课题背景2 具体实现3 数据收集和处理3 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数:2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 4 MobileNetV2网络5 损失函数softmax 交叉熵5.1 softmax函数5.2 交叉熵损失函数 6 优化器SGD7 学…

优思学院|杰克·韦尔奇谈领导的角色是什么?

杰克韦尔奇作为通用电气公司前任董事长及首席执行官的职业轨迹极为辉煌。在他的领导下,通用电气在20年的时间里市值飙升,从130亿美元跃升至高达4,800亿美元,使其成为世界上市值最高的公司之一。他一生获得了无数荣誉,被誉为“世纪…

《动手学深度学习(PyTorch版)》笔记7.5

注:书中对代码的讲解并不详细,本文对很多细节做了详细注释。另外,书上的源代码是在Jupyter Notebook上运行的,较为分散,本文将代码集中起来,并加以完善,全部用vscode在python 3.9.18下测试通过&…

电商开放API商品采集接口、关键字搜索接口,获取商品ID、商品主图接口

API是application programming interface(应用程序接口)的简称,是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件的以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。…

2024年【A特种设备相关管理(电梯)】报名考试及A特种设备相关管理(电梯)免费试题

题库来源:安全生产模拟考试一点通公众号小程序 A特种设备相关管理(电梯)报名考试是安全生产模拟考试一点通总题库中生成的一套A特种设备相关管理(电梯)免费试题,安全生产模拟考试一点通上A特种设备相关管理…

版本管理git及其命令介绍-附带详细操作

前言 在版本管理时代之前,人们写软件的方式如下图1所示 图1 无版本管理的代码 其坏处就是软件版本随着时间越来越多,每个版本修改了什么内容,修改了哪些文件,如果没有详细记录也不知道。这样久会导致如果我们想回退到某个版本内…

C语言——联合体类型

📝前言: 在前面两篇文章:C语言——结构体类型(一)和C语言——结构体(二)中,我们讲述了C语言中重要的数据类型之一:结构体类型,今天我们来介绍一下C语言中的另…

BVH动画绑骨蒙皮并在Unity上展示

文章目录 Blender绑定骨骼Blender蒙皮Blender中导入bvh文件将FBX导入Unity Blender绑定骨骼 先左上角红框进入model模式,选中要绑定的模型,然后进入Edit模式把骨骼和关节对齐。 (选中骨骼,G移动,R旋转) 为…

苹果手机如何录屏?这里告诉你答案!

苹果公司的iPhone以其卓越的性能和用户体验受到了全球消费者的喜爱,而录屏功能作为手机的一项重要功能,能够帮助我们记录手机屏幕上的操作,分享游戏技巧、制作教程视频等。本文将为您介绍苹果手机如何录屏,帮助您更好地掌握录屏技…

零售新业态,让老牧区焕发新生命

敦煌老马一声魔性“浇给”勾起了无数人对羊肉的食欲,而当大家集体涌入餐厅或者在网上下单,都想要尝一尝网红同款的时候,可能并没有想过这样一个问题——为什么在今天,即便是远离牧区的现代大城市,草原羊肉却一样能触手…

双向链表的插入、删除、按位置增删改查、栈和队列区别、什么是内存泄漏

2024年2月4日 1.请编程实现双向链表的头插&#xff0c;头删、尾插、尾删 头文件&#xff1a; #ifndef __HEAD_H__ #define __HEAD_H__ #include<stdio.h> #include<stdlib.h> #include<string.h> typedef int datatype; enum{FALSE-1,SUCCSE}; typedef str…

【宝藏系列】嵌入式入门概念大全

【宝藏系列】嵌入式入门概念大全 0️⃣1️⃣操作系统&#xff08;Operating System&#xff0c;OS&#xff09; 是管理计算机硬件与软件资源的系统软件&#xff0c;同时也是计算机系统的内核与基石。操作系统需要处理管理与配置内存、决定系统资源供需的优先次序、控制输入与输…

nvm安装node后,npm无效

类似报这种问题&#xff0c;是因为去github下载npm时下载失败&#xff0c; Please visit https://github.com/npm/cli/releases/tag/v6.14.17 to download npm. 第一种方法&#xff1a;需要复制这里面的地址爬梯子去下载&#xff08;github有时不用梯子能直接下载&#xff0c;有…

百面嵌入式专栏(技能篇)嵌入式技能树详解

沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将介绍嵌入式重点知识。 一、C语言 C语言这一块的高频考点有预处理、关键字、数据类型、指针与内存管理。 预处理有文件包含、宏定义、条件编译,其中最重要的是宏定义,通常考核宏定义的语法、宏替换与函数的区…

在每个地方都应该添加 memo 吗?

文章概叙 本文主要讲的是React中memo的使用&#xff0c;以及考虑是否使用memo的判断依据 memo介绍 memo 允许你的组件在 props 没有改变的情况下跳过重新渲染。 在使用memo将组件包装起来之后&#xff0c;我们可以‍获得该组件的一个 记忆化 版本。通常情况下&#xff0c;只要…

14.0 Zookeeper环球锁实现原理

全局锁是控制全局系统之间同步访问共享资源的一种方式。 下面介绍zookeeper如何实现全民锁&#xff0c;讲解他锁和共享锁两类全民锁。 排他锁 排他锁&#xff08;Exclusive Locks&#xff09;&#xff0c;又被称为写锁或独占锁&#xff0c;如果事务T1对数据对象O1加上排他锁…

可解释性AI(XAI)的主要实现方法和研究方向

文章目录 每日一句正能量前言主要实现方法可解释模型模型可解释技术 未来研究方向后记 每日一句正能量 当你还不能对自己说今天学到了什么东西时&#xff0c;你就不要去睡觉。 前言 随着人工智能的迅速发展&#xff0c;越来越多的决策和任务交给了AI系统来完成。然而&#xff…