C语言数组的定义和使用(非常详细)
所谓数组(Array),就是一系列数据的集合。这些数据具有相同的类型,并且在内存中挨着存放,彼此之间没有缝隙。换句话说,数组用来存放多份数据,但是它有两个要求:
如果没有数组,存储多份数据就需要多个变量名,管理和使用起来都非常麻烦;而有了数组,只需要一个变量名就能搞定。
数组包含的每份数据,都叫做数组的一个元素(Element);数组包含的数据的个数,叫做数组的长度(Length)。
来看个简单的例子:
通过下标,我们可以访问数组元素,也可以给数组元素赋值。例如,给 nums 数组中的所有元素赋值:
然后,再通过下标访问(读取)数组中的元素:
示例1:使用循环结构将数据放入数组中(也就是为数组元素逐个赋值),然后再使用循环结构输出(也就是依次读取数组元素的值),下面我们就来演示一下如何将 1~10 这十个数字放入数组中:
变量 i 既是数组下标,也是循环条件;将数组下标作为循环条件,达到最后一个元素时就结束循环。数组 nums 的最大下标是 9,也就是不能超过 10,所以我们规定循环的条件是 i<10,一旦 i 达到 10 就得结束循环。
示例2:更改上面的代码,让用户输入 10 个数字并放入数组中:
注意:访问数组元素时,下标的取值范围为 0 ≤ index < length,过大或过小都会越界,导致数组溢出,发生不可预测的情况,我们将在《C语言数组的越界和溢出》一节重点讨论,请大家务必要引起注意。
当赋值的元素少于数组总体元素的时候,剩余的元素自动初始化为 0:
我们可以通过下面的形式将数组的所有元素初始化为 0:
2) 只能给元素逐个赋值,不能给数组整体赋值。例如给 10 个元素全部赋值为 1,只能写作:
3) 如果给全部元素赋值,那么在定义数组时可以不指明数组长度,编译器会根据元素的个数自动推断出数组长度。例如:
「数组内存是连续的」这一点很重要,所以我使用了一个大标题来强调。连续的内存为指针操作(通过指针来访问数组元素)和内存处理(整块内存的复制、写入等)提供了便利,这使得数组可以作为缓存(临时存储数据的一块内存)使用。大家暂时可能不理解这句话是什么意思,等后边学了指针和内存自然就明白了。
在第 4 行代码中,因为在初始化的过程中要给所有元素赋值,所以我们没有手动指明数组的长度,而是留空,让编译器自动推断。
在第 7 行代码中,我们使用 sizeof(scores) / sizeof(scores[0]) 来计算数组长度。sizeof 是C语言中的一个关键字,专门用来获取数据或者类型的长度,也即它们在内存中所占用的字节数。sizeof(scores) 可以获取整个数组的字节数,sizeof(scores[0]) 可以获取一个元素的字节数,它们的商就是数组元素的个数(也即数组长度)。
示例4:把用户输入的数据填充到数组,然后找出数组中的最大值和最小值。
- 这些数据的类型必须相同;
- 这些数据在内存中必须连续存储。
如果没有数组,存储多份数据就需要多个变量名,管理和使用起来都非常麻烦;而有了数组,只需要一个变量名就能搞定。
数组包含的每份数据,都叫做数组的一个元素(Element);数组包含的数据的个数,叫做数组的长度(Length)。
数组不是C语言的专利,Java、C++、C#、JavaScript、PHP 等其他编程语言也有数组。
数组的定义
在C语言中,定义数组需要遵循特定的语法,一般形式如下:dataType arrayName[length];其中,dataType 是每个元素的类型,比如 int、float、char 等;dataType 是数组的名字,它是一个自定义的标识符,符合标识符的命名规则;方括号
[ ]
中的 length 是数组的长度,也即数组包含的元素的个数。来看个简单的例子:
int nums[4];这样就定义了一个名为 nums 的数组,它包含 4 个 int 类型的元素。再比如:
float temperatures[100]; //定义一个可以存储 100 个浮点数的数组 char name[20]; //定义一个可以存储 20 个字符的数组
数组下标
在C语言中,数组中的每个元素都有一个序号,这个序号从 0 开始,而不是从我们熟悉的 1 开始,称为下标(Index)。使用数组元素时,指明下标即可,形式为:arrayName[index]arrayName 为数组名称,index 为下标。例如,nums[0] 表示第 1 个元素,nums[3] 表示第 4 个元素。
通过下标,我们可以访问数组元素,也可以给数组元素赋值。例如,给 nums 数组中的所有元素赋值:
nums[0] = 20; nums[1] = 345; nums[2] = 700; nums[3] = 22; nums[4] = -59这里的 0、1、2、3、4 就是数组下标,a[0]、a[1]、a[2]、a[3]、a[4] 就是数组元素。
然后,再通过下标访问(读取)数组中的元素:
printf("%d", nums[2]);输出第 3 个元素的值,结果是 700。
示例1:使用循环结构将数据放入数组中(也就是为数组元素逐个赋值),然后再使用循环结构输出(也就是依次读取数组元素的值),下面我们就来演示一下如何将 1~10 这十个数字放入数组中:
#include <stdio.h> int main(){ int nums[10]; int i; //将1~10放入数组中 for(i=0; i<10; i++){ nums[i] = (i+1); } //依次输出数组元素 for(i=0; i<10; i++){ printf("%d ", nums[i]); } return 0; }运行结果:
1 2 3 4 5 6 7 8 9 10
变量 i 既是数组下标,也是循环条件;将数组下标作为循环条件,达到最后一个元素时就结束循环。数组 nums 的最大下标是 9,也就是不能超过 10,所以我们规定循环的条件是 i<10,一旦 i 达到 10 就得结束循环。
示例2:更改上面的代码,让用户输入 10 个数字并放入数组中:
#include <stdio.h> int main(){ int nums[10]; int i; //从控制台读取用户输入 for(i=0; i<10; i++){ scanf("%d", &nums[i]); //注意取地址符 &,不要遗忘哦 } //依次输出数组元素 for(i=0; i<10; i++){ printf("%d ", nums[i]); } return 0; }运行结果:
22 18 928 5 4 82 30 10 666 888↙ 22 18 928 5 4 82 30 10 666 888第 8 行代码中,scanf() 读取数据时需要一个地址(地址用来指明数据的存储位置),而 nums[i] 表示一个具体的数组元素,所以我们要在前边加
&
来获取地址。注意:访问数组元素时,下标的取值范围为 0 ≤ index < length,过大或过小都会越界,导致数组溢出,发生不可预测的情况,我们将在《C语言数组的越界和溢出》一节重点讨论,请大家务必要引起注意。
数组的初始化
上面的代码是先定义数组再给数组赋值,我们也可以在定义数组的同时赋值,这叫做初始化。例如:int nums[5] = {20, 345, 700, 22, -94};数组元素的值由
{ }
包围,各个值之间以,
分隔。
对于数组的初始化需要注意以下几点:
1) 可以只给部分元素赋值。当{ }
中值的个数少于元素个数时,只给前面部分的元素赋值。例如:
int a[10]={12, 19, 22 , 993, 344};表示只给 a[0]~a[4] 这 5 个元素赋值,而后面的 5 个元素自动初始化为 0。
当赋值的元素少于数组总体元素的时候,剩余的元素自动初始化为 0:
- 对于short、int、long,就是整数 0;
- 对于char,就是字符 '\0';
- 对于float、double,就是小数 0.0。
我们可以通过下面的形式将数组的所有元素初始化为 0:
int nums[10] = {0}; char str[10] = {0}; float scores[10] = {0.0};由于剩余的元素会自动初始化为 0,所以只需要给第 1 个元素赋值为 0 即可。
2) 只能给元素逐个赋值,不能给数组整体赋值。例如给 10 个元素全部赋值为 1,只能写作:
int a[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};而不能写作:
int a[10] = 1;
3) 如果给全部元素赋值,那么在定义数组时可以不指明数组长度,编译器会根据元素的个数自动推断出数组长度。例如:
int a[] = {1, 2, 3, 4, 5};等价于:
int a[5] = {1, 2, 3, 4, 5};
数组内存是连续的
数组是一个整体,它的内存是连续的;也就是说,数组元素之间是相互挨着的,彼此之间没有一点缝隙。下图演示了int a[4];
在内存中的存储情形:
「数组内存是连续的」这一点很重要,所以我使用了一个大标题来强调。连续的内存为指针操作(通过指针来访问数组元素)和内存处理(整块内存的复制、写入等)提供了便利,这使得数组可以作为缓存(临时存储数据的一块内存)使用。大家暂时可能不理解这句话是什么意思,等后边学了指针和内存自然就明白了。
综合示例
示例3:将全班同学的成绩保存在数组中,然后计算全班同学的平均成绩。#include <stdio.h> int main() { int scores[] = { 85, 92, 78, 95, 88, 60, 100, 92, 88 }; int sum = 0; float average; int length = sizeof(scores) / sizeof(scores[0]); for (int i = 0; i < length; i++) { sum += scores[i]; } average = (float)sum / length; printf("平均分数:%.2f\n", average); return 0; }输出结果:
平均分数:86.44
在第 4 行代码中,因为在初始化的过程中要给所有元素赋值,所以我们没有手动指明数组的长度,而是留空,让编译器自动推断。
在第 7 行代码中,我们使用 sizeof(scores) / sizeof(scores[0]) 来计算数组长度。sizeof 是C语言中的一个关键字,专门用来获取数据或者类型的长度,也即它们在内存中所占用的字节数。sizeof(scores) 可以获取整个数组的字节数,sizeof(scores[0]) 可以获取一个元素的字节数,它们的商就是数组元素的个数(也即数组长度)。
示例4:把用户输入的数据填充到数组,然后找出数组中的最大值和最小值。
#include <stdio.h> #define SIZE 5 int main() { int numbers[SIZE]; int i, max, min; printf("请输入 %d 个整数:", SIZE); for (i = 0; i < SIZE; i++) { scanf("%d", &numbers[i]); } max = min = numbers[0]; for (i = 1; i < SIZE; i++) { if (numbers[i] > max) { max = numbers[i]; } if (numbers[i] < min) { min = numbers[i]; } } printf("你输入的数组是:"); for (i = 0; i < SIZE; i++) { printf("%d ", numbers[i]); } printf("\n最大值:%d\n最小值:%d\n", max, min); return 0; }
输出结果(假设用户输入:23 45 12 67 9):
请输入 5 个整数:23 45 12 67 9↙ 你输入的数组是:23 45 12 67 9 最大值:67 最小值:9