C语言KR圣经笔记 5.7多维数组 5.8指针数组初始化 5.9指针vs多维数组

5.7 多维数组

C 提供了矩形的多维数组,虽然实际上它们用得比指针数组少得多。本节我们将展示多维数组的一些特性。

考虑下日期转换的问题:把某月的第几天转换为当年的第几天,以及反向转换。例如,3月1日是非闰年的第60天,是闰年的第61天。我们定义两个函数来做这个转换:day_of_year 把月和日转换为年的第几天,month_day 把年的第几天转换成月和日。因为第二个函数要计算两个值,所以月和日这两个参数会是指针:

month_day(1988, 60, &m, &d)

会将 m 设为 2,d 设为 29(2月29日)。

这两个函数需要相同的信息,每个月有多少天的表格(“七月大、八月大、九月小...”)。由于闰年和平年月份的天数不一样,把这些数据分开放到二维数组的两行,会比计算时去判断二月份的天数要简单。数组和执行转换的函数如下:

static char daytab[2][13] = {{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

int day_of_year(int year, int month, int day)
{int i, leap;leap = year%4 == 0 && year%100 != 0 || year%400 == 0;for (i = 0; i < month; i++)day += daytab[leap][i];return day;
}void month_day(int year, int yearday, int *pmonth, int *pday)
{int i, leap;leap = year%4 == 0 && year%100 != 0 || year%400 == 0;for (i = 1; yearday > daytab[leap][i])yearday -= daytab[leap][i];*pmonth = i;*pday = yearday;
}

回忆一下,逻辑表达式的算术值,不是0(假)就是1(真),所以能赋给 leap,用作数组 daytab 的下标。

对 day_of_year 和 month_day 而言,数组 daytab 只能是外部的,这两个函数才能共用它。我们把它的类型定义为 char, 是为了说明,用 char 来保存非字符的小整数,是一种正当的用法。【21世纪的应用程序员应该不用这么节省内存了】

daytab 是我们处理的第一个二维数组。在 C 中,二维数组实际上是一维数组,其【高维的】每个元素是一个数组。这样下标就能写成

daytab[i][j]    /* [列][行] */

而不是

daytab[i, j]    /* 错误 */

除了这个表示法上的区别,二维数组的处理方式与其他编程语言差不多是一样的。元素是按行来存的,因此最右边的下标,或者说列,在以存储顺序访问数组元素时,变化最快。

一维数组以大括号内的一个初始化表达式列表来初始化;二维数组的每行以对应的子列表来初始化。我们让 daytab 从第 0 列开始,这样月份就自然地从 1 到 12 而不是 0 到 11。因为这里内存空间并不稀缺,这种写法比调整下标会更清晰。

如果二维数组要传递给函数,函数中的参数声明必须包含列数;行数是无关紧要的,因为依旧传递的是一个指向行的指针,而每行是一个数组。在这个特例中,该指针指向的对象是这些包含 13 个 int 的数组。因此,如果 daytab 要传递给函数 f,f 的声明会是

f(int daytab[2][13]) { ... }

或是

f(int daytab[][13]) { ... }

既然行数无关紧要,也能写成

f(int (*daytab)[13]) { ... }

说明参数是指向有 13 个整数的数组的指针。括号是必须的,因为中括号 [] 的优先级比 * 号高。如果没有括号,如下声明

int *daytab[13]

是一个数组,包含13个整数指针。更通用地说【即针对所有多维数组】,数组的第一维(下标)是随意的,其他维度必须指定。

5.12节有对复杂声明的进一步讨论。

练习5-8、dat_of_year 和 month_day 中没有错误校验。修改这个缺陷。

5.8 指针数组初始化

考虑这个问题:写个函数 month_name(n) ,返回包含第 n 个月名字的字符串指针。这是内部 static 数组的理想应用场景。month_name 包含一个私有的字符串数组,当它被调用时会返回指向正确字符串的指针。本节展示了这个包含月份名的数组是如何初始化的。

语法与之前初始化语法类似:

/* month_name:返回第n个月的名字 */
char *month_name(int n)
{static char *name[] = {"Illegal month","January", "February", "March","April", "May", "June","July", "August", "September","October", "November", "December"};return (n < 1 || n > 12) ? name[0] : name[n];
}

name 是一个字符串指针的数组,它的声明与前面文本行排序例子中 lineptr 的声明一样。初始化表达式是一列字符串;每个字符串都被赋给数组中对应的位置。第 i 个字符串存放在内存的某处,而指向它们的指针存在了 name[i] 中。由于数组名 name 的长度没有指定,编译器会计算初始化表达式,并填充正确的数值。

5.9 指针vs多维数组

C 初学者有时会搞不清二维数组与指针数组之间的区别,比如前一个例子中的 name。给出如下定义

int a[10][20];
int *b[10];

则 a[3][4] 和 b[3][4] 在语法上都是合法的,都是对一个 int 的引用。但 a 是一个真正的二维数组:有 200 个 int 大小的内存已经被分配了,而且使用传统的矩形下标计算公式 20*行+列 来找数组元素 a[行][列] 。然而,对 b 来说,这个定义只分配了10个指针的空间,而且没有初始化;必须显式初始化,不管是静态初始化或是通过代码来初始化。 假定 b 的每个元素的确指向了一个有20个元素的数组,则总共需要有200个 int 被分配,再加上 10 个内存单元用来放指针。指针数组的重要优势在于,数组的每行可以是不同的长度。也就是说, b 的每个元素不需要都指向有 20个元素的向量【数组】;某些可以指向有 2个元素的,或者有 50个元素的,甚至不指向任何东西【NULL】

虽然我们上面是拿整数来讨论,但目前为止最常用的指针数组是用来保存不同长度的字符串,例如函数 month_name 中保存各月份名称的 name 数组。指针数组的声明和图示如下:

char *name = {"Illegal month", "Jan", "Feb", "Mar"};

对比二维数组的声明和图

char aname[][15] =  {"Illegal month", "Jan", "Feb", "Mar"};

练习5-9、重写 day_of_year 和 month_day,但使用指针而不是索引。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DGL离线安装

https://data.dgl.ai/wheels/repo.htmlhttps://data.dgl.ai/wheels/repo.html pip install dgl-cu1110.8.2 -f https://data.dgl.ai/wheels/repo.html

Spring Cloud Alibaba 介绍与版本映射关系

目录 前言 一、Spring Cloud Alibaba 是什么&#xff1f; 二、Spring Cloud Alibaba 版本依赖 前言 Spring Cloud 本身并不是一个拿来即可用的框架&#xff0c;它是一套微服务规范&#xff0c;这套规范共有两代实现。 (子项目)&#xff1a; ● 第一代实现&#xff1a; Spr…

linux usb 驱动介绍

通用串行总线(USB)是一个在主机和许多外设之间的连接. 最初它被创建来替代许多慢速和不同的总线- 并口, 串口, 和键盘连接--有一个单个的所有设备都可以连接的总线类型. [45] USB 已经成长超出了这些 慢速连接并且现在支持几乎每种可以连接到 PC 的设备. USB 规范的最新版本增加…

stable diffusion 基础教程-图生图

界面 图生图大概有以下几个功能: 图生图涂鸦绘制局部绘制局部绘制(涂鸦蒙版)其常用的也就上面四个,接下来逐步讲解。 以图反推提示词 图生图可以根据反推提示词来获取相应图片的提示词,目前3种主流方式,如下: CLIP反推提示词:推导出的文本倾向于自然语言的描述方式,…

vue2 element 弹出框拖拽会出现一层阴影问题

问题如图所示&#xff1a; 因增加 draggable 属性导致我弹窗表单清空文本框时&#xff0c;从右向左选中字体会出现拖拽阴影效果 去掉 draggable 即可 <template><div class"sys-jobTrigger-container"><el-dialog:visible.sync"state.isShowD…

基于Kettle开发的web版数据集成开源工具(data-integration)-应用篇

目录 &#x1f4da;第一章 基本流程梳理&#x1f4d7;页面基本操作&#x1f4d7;对应后台服务流程 &#x1f4da;第二章 二开思路&#x1f4d7;前端&#x1f4d7;后端 &#x1f53c;上一集&#xff1a;基于Kettle开发的web版数据集成开源工具(data-integration)-介绍篇 *️⃣主…

三、Kubernetes(K8s)入门(一)

视频教程连接k8s 入门到微服务项目实战.xmind链接&#xff1a;https://pan.baidu.com/s/1q04euH7baE8eXNyG3kPPbA 提取码&#xff1a;jej4比较好的笔记 kubectl命令的语法如下&#xff1a; kubectl [command] [type] [name] [flags]comand&#xff1a;指定要对资源执行的操作…

java基于ssm框架的滁艺咖啡在线销售系统+vue论文

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装滁艺咖啡在线销售系统软件来发挥其高效地信息处理的作用&am…

RabbitMQ安装与应用

文章目录 1. RabbitMQ1.1. 同步通讯与异步通讯1.2. 异步通讯的优缺点1.3. 几种MQ的对比1.4. docker安装运行RabbitMQ 流程1.5. RabbitMQ的几个概念1.6. 五种模型1.6.1. 基本消息队列 1.7. 基本使用1.7.1. 1建立连接时会出现以下界面![在这里插入图片描述](https://img-blog.csd…

零知识证明(zk-SNARK)- groth16(一)

全称为 Zero-Knowledge Succinct Non-Interactive Argument of Knowledge&#xff0c;简洁非交互式零知识证明&#xff0c;简洁性使得运行该协议时&#xff0c;即便 statement 非常大&#xff0c;它的 proof 大小也仅有几百个bytes&#xff0c;并且验证一个 proof 的时间可以达…

代码随想录刷题第三十六天| 435. 无重叠区间 ● 763.划分字母区间 ● 56. 合并区间

代码随想录刷题第三十六天 无重叠区间 (LC 435) 题目思路&#xff1a; 代码实现&#xff1a; class Solution:def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:intervals.sort(keylambda x: (x[0],x[1]))count 0right intervals[0][1]for i in ra…

互联网加竞赛 基于CNN实现谣言检测 - python 深度学习 机器学习

文章目录 1 前言1.1 背景 2 数据集3 实现过程4 CNN网络实现5 模型训练部分6 模型评估7 预测结果8 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于CNN实现谣言检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&am…