今天哈尔滨下雪了( ╯▽╰),对,所以我们一起接着学C语言吧。什么?雪景照片?嗯( -'`-)……
今天我们要讲的是数组,一言以蔽之,数组就是存储一批同类型数据的地方。
在 c 语言中往往需要先定义再使用,数组的定义方式如下
在定义数组时,需要在数组名后边紧跟着一对方括号,其中用常量表达式来指定数组中元素的个数。因为只有告诉编译器元素的个数,编译器才能申请对应大小的内存给它存放。比如说 a[6] 在内存中占用的大小就是 int 型占用的大小再乘以6。
那么问题来了,上面定义的三种数组占用了多少字节呢?知道的同学可以在留言区抢答哦,不知道的同学可以快速回顾下4. C语言 -- 一个由数据类型和取值范围引发的 BUG,然后在假装自己是抢答的哦。
在定义数组的同时对其各个元素进行赋值,称之为数组的初始化,主要有以下五种方式
将数组中首位初始化为某个数值,其余位置初始化为 0
在这种方式中有一点需要注意,其中 {0} 并不是将数组内的所有元素初始化为 0 的含义,而是数组中的第一个元素的值为 0,剩余元素值为 0 的意思。因此通过 int a[10] = {3} 的数组,实际上只有第一个数字为 3 ,其余数字均为0。
只给一部分元素赋值,未被赋值的元素自动初始化为 0
所以第一种方法实际上是着一种方法的特例。
如果是赋予不同的值,那么用逗号分隔开即可
只给出各个元素的值,而不指定数组的长度
指定初始化的元素
C99 增加了一种新特性——指定初始化的元素。这样就可以只对数组中的某些指定元素进行初始化赋值,而未被赋值的元素自动初始化为 0。
![640?wx_fmt=png](https://i-blog.csdnimg.cn/blog_migrate/bf2b5e58569f07f4ec3225b4630b6bbe.png)
区分两种初始化的方法
在上面的初始化方法中,要注意区分第二种和第四种初始化的方法。其中第二种初始化的方法已经指定了数组的长度,因此即使只给出前面一部分的数字,也会自动补充后面的数字;但是第四种方法是通过 {} 给出元素自动确定数组的长度。比如说下面这两种初始化的方式
所以访问 b[6] 就会发生数组越界,要么报错,要么得到一个随机数(具体是那种情况根据编译器而定)。
![640?wx_fmt=png](https://i-blog.csdnimg.cn/blog_migrate/bf2b5e58569f07f4ec3225b4630b6bbe.png)
未初始化直接访问
如果现在只定义了一个数组,但是没有给数组进行初始化,那么会出现什么样的情况呢,比如说下面的这段代码
会得到如下的输出
会得到一堆乱起八糟的数据,这是为什么呢?这主要涉及到栈结构中的数据是随机的,栈结构的内容会在后面进行介绍。
在 C99 标准中,数组的尺寸如果是整型常量或者整型常量表达式,或者确定他的尺寸的时候,他就不是一种长度可变的数组,相反(即指其余条件下)则是一个长度可变的数组。即在 C99 标准中,c 语言已经支持变长数组了。注意这里的变长指的是数组的长度是在运行的时候才会被决定,就是说可以用一个变量来指定组的长度。
上面是在 C99 的标准中关于变长数组的说明,接着我们来一下,如果使用了 C99 标准中的变长数组是否可以使用 gcc 来进行编译呢?答案是可以的。因为作为编译器的扩展,gcc 在 C90 模式下和 C++ 模式下都是遵守 C99 标准的。所以 gcc 即使没有加上 -std=c99,就是他默认以 C90 标准进行编译的时候,也是遵守 C99 标准的,所以使用变长数组的时候,不用加上 -std=c99 也是可以正常运行的。所以下面代码是合法的
在这段代码中,我们使用了一个整型的变量 n+1 来表示数组的长度,仍然可以编译通过并执行,说明变长数组确实是可用的。
但是在这段代码中,仍有几个部分是需要注意的。首先是数组的长度,通过第 7、8 两行可以知道输入的字符个数应该是 n 个 ,但是为什么数组的长度会定义为 n+1 呢?这主要是因为字符串都是以 '\0' 结尾的,但是 '\0' 又不可能通过用户来输入,所以要定义一个 n+1 长度的数组,然后自己在里面加入'\0' 作为字符串的结尾。
其次我们在第 13 行加入了 getchar(); ,这主要是将标准输入流中剩下的 '\n' 扔掉。之所以 '\n' 会在输入缓冲区可以参考 《5. 很“迷”的字符与字符串》 3.3 三种输入函数的注意事项 中的内容。读取字符时scanf() 以 Enter 结束一次输入,不会舍弃最后的回车符。所以如果不使用 getchar(); 会造成首先读入键盘缓冲区的回车符,之后再读入数组中的字符,会导致数组中的最后一个字符无法正确显示。
数组的访问方式很直觉,即通过下标进行访问;但是比较反直觉的是,数组的下标从 0 开始,所以某个数组中第一个元素的下标是 0 而不是 1。所以在第一张图中,每一行中的数字实际上是数组的下标。
访问数组具体方式如下
有两点个需要注意,首先定义数组与访问数组的写法十分相似,区别在于定义数组需要申明数组内变量的类型,但是访问不需要;在访问数组的过程中要注意数组越界的问题,如下
当数组发生越界情况时,本身在 C语言中属于未定义行为,不同的编译器可能会有不同的对待方式。比如说在 gcc 中,会随机给越界访问的数组元素分配一个数字。
实现一个执行10次的循环,我们通常下面的第一种写法,而不是第二种写法
这是因为我们常常需要使用循环来访问数组,而数组的下标是从 0 开始的。
如果我们采用只给出各个元素的值,而不指定数组的长度的方法来初始化数组,比如说 int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};,这个时候我们并不知道数组的长度。如果要写一个 for 循环来遍历数组中的每一个元素,应该使用下面这种方法
使用这样的方法表达数组的长度是在实际开发中是很常见的技巧。
好啦今天的内容就到这里,想说什么都可以留言哦。从今天开始就已经算是进入指针相关的部分了呢,之后的内容可能会变难,但是我会尽力讲明白的,也希望大家继续支持我!
[1] “小甲鱼” 视频课程《带你学C带你飞》【第一季】P17 18
![640?wx_fmt=png](https://i-blog.csdnimg.cn/blog_migrate/41a14a2dd726e2f3f15338550da2db8b.png)
![640?wx_fmt=png](https://i-blog.csdnimg.cn/blog_migrate/41a14a2dd726e2f3f15338550da2db8b.png)
欢迎大家关注我的知乎号(左侧)和经常投稿的微信公众号(右侧)