C语言数组的定义和使用(非常详细)

 
所谓数组(Array),就是一系列数据的集合。这些数据具有相同的类型,并且在内存中挨着存放,彼此之间没有缝隙。换句话说,数组用来存放多份数据,但是它有两个要求:
  • 这些数据的类型必须相同;
  • 这些数据在内存中必须连续存储。

如果没有数组,存储多份数据就需要多个变量名,管理和使用起来都非常麻烦;而有了数组,只需要一个变量名就能搞定。

数组包含的每份数据,都叫做数组的一个元素(Element);数组包含的数据的个数,叫做数组的长度(Length)

数组不是C语言的专利,JavaC++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];在内存中的存储情形:
C语言数组的内存分布

「数组内存是连续的」这一点很重要,所以我使用了一个大标题来强调。连续的内存为指针操作(通过指针来访问数组元素)和内存处理(整块内存的复制、写入等)提供了便利,这使得数组可以作为缓存(临时存储数据的一块内存)使用。大家暂时可能不理解这句话是什么意思,等后边学了指针和内存自然就明白了。

综合示例

示例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