C语言小课堂:数组

同学们,今天我们来学习C语言中的一个非常基础且重要的数据结构——数组。

  1. 什么是 数组

在C语言中,数组 是一种同类型数据的集合,它们在内存中是连续存储的,并且可以通过一个统一的名称和**索引(下标)**来访问其中的每个元素。

想象一下你有一排整齐的盒子,每个盒子里都放着同样类型的东西(比如都是苹果,或者都是书)。数组就像这一排盒子,每个盒子就是一个数组元素,而盒子的编号(从0开始)就是它的索引。

同类型数据:这意味着一个数组只能存储一种数据类型(例如,一个 int 数组只能存储整数,一个 double 数组只能存储双精度浮点数)。

连续存储:数组的各个元素在内存中是紧挨着存放的。这种特性使得数组访问效率很高。

统一名称:整个集合有一个共同的名字,比如 scoresgrades

索引访问:通过在数组名后加上方括号 [] 和一个整数(索引),我们可以精确地访问到数组中的某个特定元素。C语言中的数组索引总是从 0 开始。

示例:一个存储5个整数的数组 numbersnumbers[0] 是第一个元素numbers[1] 是第二个元素 …numbers[4] 是第五个元素

  1. 为什么要使用 数组

当你需要处理 大量同类型数据 时,如果为每个数据都声明一个独立的变量,会非常麻烦且效率低下。数组就是为了解决这个问题而生的。

何时用:

存储一系列相关数据:比如一个班级的学生分数、一个星期的每日温度、一系列的传感器读数等。

需要批量处理数据:当你要对这些数据进行遍历、查找、排序等操作时,数组的结构使得这些操作变得简单高效。

作为函数参数传递:可以将整个数组作为参数传递给函数,方便地对数据进行处理。

为什么用:

简化代码:无需声明多个独立的变量,用一个数组名即可管理所有相关数据。

提高效率:由于内存的连续性,程序可以更快地访问数组元素。

便于数据管理:通过循环结构,可以轻松地对数组中的所有或部分元素进行操作。

和关联知识点的联系和区别:

与普通变量的区别:普通变量一次只能存储一个值,数组可以存储多个值。

与结构体(Struct)的区别:结构体可以存储不同类型的数据项,而数组只能存储同类型的数据项。

与指针的联系:数组名在C语言中常常被视为指向其第一个元素的常量指针,这使得数组和指针之间有着紧密的联系,理解这一点对深入学习C语言非常重要。

  1. 怎么使用 数组

使用数组主要包括以下几个步骤:声明、初始化和访问。

3.1 数组的声明

声明数组时需要指定数组的类型和大小(元素个数)。语法:数据类型 数组名[数组大小];

示例:

int scores[10]; // 声明一个可以存储10个整数的数组
double temperatures[7]; // 声明一个可以存储7个双精度浮点数的数组
char name[20]; // 声明一个可以存储20个字符的数组(可以用来存储字符串)

3.2 数组的初始化

数组可以在声明时进行初始化,也可以在声明后单独赋值。

声明时初始化:

完全初始化:

int numbers[5] = {10, 20, 30, 40, 50}; // 声明并初始化一个包含5个整数的数组

部分初始化:如果初始化列表中的元素少于数组大小,剩余的元素会被自动初始化为0(对于数值类型)或空字符(对于字符类型)。

int nums[5] = {1, 2}; // nums[0]=1, nums[1]=2, nums[2]=0, nums[3]=0, nums[4]=0

不指定大小(编译器自动计算):

int data[] = {100, 200, 300}; // 数组大小自动确定为3
char greeting[] = "Hello"; // 数组大小自动确定为6(包含末尾的'\0'空字符)

声明后赋值:通过索引逐个赋值。

int arr[3];
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;

3.3 数组元素的访问

通过索引(下标)来访问数组中的单个元素。记住,C语言数组的索引从 0 开始,到 数组大小 - 1 结束。

语法: 数组名[索引];

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

int main() {
int scores[5] = {85, 90, 78, 92, 88};

// 访问第一个元素 (索引为0)
printf("第一个分数是: %d\n", scores[0]); // 输出: 第一个分数是: 85

// 修改第三个元素 (索引为2)
scores[2] = 80;
printf("修改后的第三个分数是: %d\n", scores[2]); // 输出: 修改后的第三个分数是: 80

// 遍历数组并打印所有元素
printf("所有分数:\n");
for (int i = 0; i < 5; i++) {
printf("scores[%d] = %d\n", i, scores[i]);
}

return 0;
}

3.4 数组的越界访问

这是一个非常重要的注意点! C语言不会检查数组访问是否越界。如果你尝试访问 数组大小 - 1 以外的索引(例如 scores[5] 在一个大小为5的数组中),编译器可能不会报错,但程序运行时会访问到不属于该数组的内存区域,这会导致:

未定义的行为(Undefined Behavior):程序可能崩溃,或者产生意想不到的错误结果,甚至可能被恶意利用。

难以调试:这类错误通常很难发现。

务必确保你的索引始终在 0数组大小 - 1 的有效范围内。

理解并熟练掌握数组是C语言编程的基石,它为你处理批量数据和构建更复杂的数据结构(如字符串、矩阵等)打下了坚实的基础。

💻 配套习题

第一题 (选择题)

以下关于C语言数组的描述,哪一项是 错误 的?

A. 数组中的所有元素必须是相同数据类型。

B. 数组元素在内存中是连续存储的。

C. 数组的索引(下标)总是从 1 开始。

D. 声明数组时必须指定其大小,除非在声明时进行完全初始化。

答案: C

第二题 (填空题)

请填写代码中的下划线部分,使得程序能够声明一个名为 ages 的数组,它能存储5个整数,并将所有元素初始化为 0

#include <stdio.h>
int main() {
int ages[5] = {_____};
printf("%d\n", ages[2]); // 应该输出 0
return 0;
}

答案: {0}{0, 0, 0, 0, 0}

第三题 (代码输出题)

阅读下面的C代码,并写出其输出结果:

#include <stdio.h>
int main() {
char greeting[] = "Hello";
printf("%c%c%c\n", greeting[0], greeting[4], greeting[5]);
return 0;
}

答案: Ho

第四题 (概念辨析题)

判断题:在C语言中,声明一个大小为 N 的数组 int arr[N]; 后,可以安全地访问 arr[N] 这个元素。

A. 正确 B. 错误

答案: B

第五题 (编程题)

编写一个C程序,声明一个包含4个整数的数组 numbers。将数组的第一个元素设置为 10 ,第二个元素设置为 20,第三个元素设置为 30 ,第四个元素设置为 40。然后,计算并输出数组中所有元素的 和。

答案: 见下方代码及讲解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

int main() {
int numbers[4]; // 声明一个包含4个整数的数组
int sum = 0; // 用于存储和的变量

// 赋值
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
numbers[3] = 40;

// 计算和
for (int i = 0; i < 4; i++) {
sum += numbers[i]; // 将每个元素加到sum中
}

// 输出结果
printf("数组元素的总和是: %d\n", sum);

return 0;
}

💡 习题讲解

第一题讲解

答案: C

讲解:

A. 数组中的所有元素必须是相同数据类型。 这是数组的定义之一,数组是同类型数据的集合。正确。

B. 数组元素在内存中是连续存储的。 这是数组的重要特性,也是其高效访问的基础。正确。

C. 数组的索引(下标)总是从 1 开始。错误。C语言中的数组索引总是从 0 开始。例如,一个大小为5的数组,其有效索引是 0, 1, 2, 3, 4

D. 声明数组时必须指定其大小,除非在声明时进行完全初始化。 如果不指定大小且不初始化,编译器无法知道需要分配多少内存。但如果像 int arr[] = {1, 2, 3}; 这样完全初始化,编译器可以根据初始化列表自动推断大小。正确。

易混淆点:许多其他编程语言(如Pascal、Fortran)的数组索引可能从1开始,但C/C++、Java、Python等语言都从0开始。这是C语言的约定,务必牢记。

第二题讲解

答案: {0}{0, 0, 0, 0, 0}

讲解:

int ages[5] = {0};:这是一个C语言中初始化数组的特殊用法。当初始化列表中的元素少于数组大小时,剩余的元素会被自动初始化为 0(对于数值类型)。所以,ages[0] 被初始化为 0 ,而 ages[1]ages[4] 也都会自动被初始化为 0

int ages[5] = {0, 0, 0, 0, 0};:这种方式是显式地将所有元素都初始化为 0 ,效果与 {0} 相同,但更冗长。

提示:利用 {0} 来初始化整个数组为0是一个非常方便且常用的技巧。

第三题讲解

答案: Ho

讲解:

char greeting[] = "Hello";:声明并初始化一个字符数组 greeting。字符串 “Hello” 实际上是 H , e , l , l , o , \0 这6个字符。因此,greeting 数组的大小是6。

greeting[0] 是 ‘H’

greeting[1] 是 ‘e’

greeting[2] 是 ‘l’

greeting[3] 是 ‘l’

greeting[4] 是 ‘o’

greeting[5]\0 (空字符,字符串的结束标志)

printf("%c%c%c\n", greeting[0], greeting[4], greeting[5]);

%cprintf 用来输出单个字符的格式说明符。

greeting[0] 对应第一个 %c,输出 ‘H’。

greeting[4] 对应第二个 %c,输出 ‘o’。

greeting[5] 对应第三个 %c,输出 \0

\0 是一个空字符,它是不可见的,但它是一个有效的字符。当 printf 遇到 %c 格式符打印 \0 时,通常不会显示任何可见字符,但它确实被“打印”了。在某些终端上可能会显示为空格,但在标准输出中,它通常不产生可见输出。然而,题目给出的答案 Ho 暗示 \0 在输出时被隐式忽略或不显示。在实际环境中,它的行为取决于终端和 printf 的具体实现,但通常我们认为它不产生可见输出。

所以,最终输出是 H 后面跟着 o,再跟着一个不可见的 \0。因此,结果看起来就是 “Ho”。

第四题讲解

答案: B (错误)

讲解:

原命题是:“在C语言中,声明一个大小为 N 的数组 int arr[N]; 后,可以安全地访问 arr[N] 这个元素。”

错误。C语言数组的索引范围是从 0N-1。因此,对于一个大小为 N 的数组 arr ,有效的索引是 arr[0], arr[1], ..., arr[N-1]。尝试访问 arr[N] 属于 数组越界访问。

危害:越界访问会导致“未定义行为”。这意味着程序可能会:

崩溃(Segmentation Fault)。

读取或写入到相邻内存区域的数据,导致数据损坏。

产生看似随机但难以追踪的错误结果。

在某些情况下,甚至可能被黑客利用来执行恶意代码。

提示:始终检查你的循环条件和索引计算,确保它们不会超出数组的有效边界。例如,遍历一个大小为 N 的数组,循环条件应该是 i < N 而不是 i <= N

第五题讲解

答案: 见上方代码及讲解

讲解:

包含头文件:#include <stdio.h> 是必需的,因为它包含了 printf 函数的声明,这是我们用来输出结果的。

声明数组:

int numbers[4];:这行代码声明了一个名为 numbers 的整数数组,它能够存储4个 int 类型的值。此时数组中的值是未知的(垃圾值),因为它没有被初始化。

声明和初始化求和变量:

int sum = 0;:声明一个整数变量 sum 并将其初始化为 0。这个变量将用于累加数组中所有元素的和。

为数组元素赋值:

numbers[0] = 10;

numbers[1] = 20;

numbers[2] = 30;

numbers[3] = 40;这些行通过数组的索引(0到3)分别给数组的四个元素赋值。

计算和:

int i = 0; :循环变量 i 从0开始,这正是C语言数组的起始索引。

i < 4; :循环条件确保 i 的值不会达到或超过数组的大小(4),从而避免了越界访问。当 i 等于 3 时,循环体还会执行一次,因为 3 < 4 为真。当 i 变为 4 时,4 < 4 为假,循环停止。

i++ :每次循环结束后, i 递增1,以便访问下一个元素。

for (int i = 0; i < 4; i++) { ... }:这里使用了一个 for 循环来遍历数组的所有元素。

sum += numbers[i];:在循环体内,将当前索引 i 对应的数组元素 numbers[i] 的值加到 sum 变量中。

输出结果:

printf("数组元素的总和是: %d\n", sum);:使用 printf 函数输出计算得到的总和。%d 是用于输出整数的格式说明符。

这个编程题综合考察了数组的声明、赋值、通过循环遍历以及基本的算术运算。这是C语言数组操作中非常典型的场景。