目录
第八章 数组
8.1 一维数组
8.1.1 数组下标
8.1.2 数组初始化
8.1.3 指定初始化式
8.1.4 对数组使用 sizeof 运算符
8.2 多维数组
8.2.1 多维数组初始化
8.2.2 常量数组
8.3 C99 中的变长数组
问与答
各书的读书笔记已经陆陆续续开展了哈(本书为《C语言程序设计现代方法(第二版)》)(一本书一个专栏,订阅会第一时间推荐更新哈),主要会把作者认为比较重要或者比较新奇的知识点记录下来。但是要想真的了解一本书,自己去看可能才会有更深的体会哈。
C语言的入门篇进阶篇和深剖篇都整理在这里了哈。然后这里是个人主页,比点头像更好找文章哈。
作者新建立的社区:非科班转码社区-CSDN社区云
社区微信公众号:
期待hxd的支持哈
最后是打鸡血环节:改变的确很难,但结果值得冒险,拿出点勇气来。路还很长,现在才刚开始而已。过去无可挽回,未来可以改变。
第八章 数组
我们所见的变量都只是 标量(scalar ):标量具有保存单一数据项的能力。C语言也支持 聚合 (aggregate )变量,这类变量可以存储一组一组的数值。在 C 语言中一共有两种聚合类型: 数组 (array)和结构(structure )。
8.1 一维数组
数组是含有多个数据值的数据结构,并且每个数据值具有相同的数据类型。这些数据值称 为 元素(element)。 数组的元素可以是任何类型,数组的长度可以用任何(整数)常量表达式( 5.3节)指定。因为程序以后改变时可能需要调整数组的长度,所以较好的方法是用宏来定义数组的长度:
8.1.1 数组下标
为了 存取特定的 数组元素,可以在写数组名的同时在后边加上一个用 方括号围绕的整数值 (称这是对数组 取下标(subscripting )或 进行 索引(indexing ))。
8.1.2 数组初始化
像其他变量一样,数组也可以在声明时获得一个初始值。 数组初始化式(array initializer)最常见的格式是一个用大括号括起来的常量表达式列表,常量表达式之间用逗号进行分隔:
如果初始化式比数组短,那么数组中剩余的元素赋值为0:利用这一特性,可以很容易地把数组初始化为全0:
初始化式完全为空是非法的,所以要在大括号内放上一个0。初始化式比要初始化的数组长也是非法的。
如果给定了初始化式,可以省略掉数组的长度:
编译器利用初始化式的长度来确定数组的大小。数组仍然有固定数量的元素(此例中为10),这跟明确地指定长度效果一样。
8.1.3 指定初始化式
经常有这样的情况:数组中只有相对较少的元素需要进行显式的初始化,而其他元素可以 进行默认赋值。考虑下面这个例子:
C99中的指定初始化式(指示器)可以用于解决这一问题。上面的例子可以使用指定初始化式写为:
括号中的数字称为指示符,方括号和其中的常量表达式一起,组成一个指示器。
除了可以使赋值变得更简短、更易读之外,指定初始化式还有一个优点:赋值的顺序不再 是一个问题,我们也可以将先前的例子重新写为: 指示符必须是整型常量表达式。如果待初始化的数组长度为 n,则每个指示符的值都必须在 0和 n1之间。但是,如果数组的长度是省略的,指示符可以是任意非负整数;对于后一种情况,编译器将根据最大的指示符推断出数组的长度。在接下来的这个例子中,指示符的最大值为23,因此数组的长度为24:
初始化式中可以同时使用老方法(逐个元素初始化)和新方法(指定初始化式):
而没有指定值的元素均赋予默认值0。
8.1.4 对数组使用 sizeof 运算符
运算符sizeof可以确定数组的大小(字节数)。如果数组a有10个整数,那么sizeof(a)通常为40(假定每个整数占4个字节)。 PS:sizeof求空间 strlen求长度,不包括’\0’。
8.2 多维数组
数组可以有任意维数。 数组的行和列下标都从0开始索引。
C语言是按照行主序存储数组的,也就是从第0行开始,接着第1行,依次类推。
通常我们会忽略这一细节,但有时它会对我们的代码有影响。
就像for循环和一维数组紧密结合一样,嵌套的for循环是处理多维数组的理想选择。
8.2.1 多维数组初始化
C99的指定初始化式对多维数组也有效。例如,可以这样创建2×2的单位矩阵:
像通常一样,没有指定值的元素都默认置为0。
8.2.2 常量数组
无论一维数组还是多维数组,都可以通过在声明的最开始处加上单词const而成为“常量” 程序不应该对声明为const的数组进行修改,编译器能够检测到直接修改某个元素的意图。 把数组声明为const 有两个主要的好处。 1.它表明程序不会改变数组,这对于以后阅读程序 的人可能是有价值的信息。 2.它还有助于编译器发现错误——const会告诉编译器我们不打算修 改数组。 const类型限定符( 18.3节)不限于数组,后面将看到,它可以和任何变量一起使用。但是,const在数组声明中特别有用,因为数组经常含有一些在程序执行过程中不会发生改变的参考信息。
8.3 C99 中的变长数组
上面程序中的数组a是一个变长数组(variable-length array,简称VLA)。 变长数组的长度是 在程序执行时计算的,而不是在程序编译时计算的。 变长数组的主要优点是程序员不必在构造数组时随便给定一个长度,程序在执行时可以准确地计算出所需的元素个数。如果让程序员来指定长度,数组可能过长(浪费内存)或过短(导致程序出错)。
问与答
问:为什么数组下标从0开始而不是从1开始?(p.113) 答:让下标从0开始可以使编译器简单一点。而且,这样也可以使得数组取下标运算的速度有少量的提高。
问:如果希望数组的下标从1到10而不是从0到9,该怎么做呢?
答:这有一个常用的窍门:声明数组有11个元素而不是10个元素。这样数组的下标将会从0到10,但是可以忽略掉下标为0的元素。
问:使用字符作为数组的下标是否可行呢?
答:是可以的, 因为 C 语言把字符作为整数来处理。但是,在使用字符作为下标前,可能需要对字符进行“缩放”。举个例子,假设希望数组letter_count对字母表中的每个字母进行跟踪计数。这个数组将需要26个元素,所以可采用下列方式对其进行声明: int letter_count[26]; 然而,不能直接使用字母作为数组 letter_count的下标,因为字母的整数值不是落在0~25 的区间内的。为了把小写字母缩放到合适的范围内,可以简单采用减去’a’的方法;为了缩放大写字母,则可以减去’A’。 例如,如果ch含有小写字母,为了对相应的计数进行清零操作,可以这样写: letter_count[ch-‘a’] = 0; 说明一下,这种方法不一定可移植,因为它假定字母的代码是连续的。不过,对大多数字符集(包括ASCII)来说,这样做都是没问题的。
问:指定初始化式可能会对同一个数组元素进行多次初始化操作。考虑下面的数组声明:
int a[] = {4, 9, 1, 8, [0] = 5, 7};
这个声明是否合法?如果合法,数组的长度是多少?(p.116)
答:这个声明是合法的。下面是它的工作原理:编译器在处理初始化式列表时,会记录下一个待初始化的数组元素的位置。 正常情况下,下一个元素是刚被初始化的元素后面的那个。但是,当列表中出现初始化式时,下一个元素会被强制为指示符对应的元素,即使该元素已经被初始化了。
问:如果试图用赋值运算符把一个数组复制到另一个数组中,编译器将给出出错消息。哪里错了?
答:赋值语句 看似合理,但它确实是非法的。非法的理由不是显而易见的,这需要用到C语言中数组和指针之间的特殊关系,这一点将会在第12章进行探讨。把一个数组复制到另一个数组中的最简单的实现方法是利用循环对数组元素逐个进行复制:
另一种可行的方法是使用来自头的函数memcpy(意思是“内存复制”)。memcpy函数( 23.6节)是一个底层函数,它把字节从一个地方简单复制到另一个地方。为了把数组b复制到数组a中,使用函数memcpy的格式如下:
许多程序员倾向于使用memcpy函数(特别是处理大型数组时),因为它潜在的速度比普通循环更快。
*问:6.4节提到,C99不允许goto语句绕过变长数组的声明。为什么会有这一限制呢?
答:在程序执行过程中, 遇到变长数组声明时通常就为该变长数组分配内存空间了。用goto语句绕过变长数组的声明 可能会导致程序 对未分配空间的数组中的元素进行访问。
最后的最后,创作不易,希望读者三连支持
赠人玫瑰,手有余香