第九章 数组
9.1 数组的概念
9.1.1为什么需要数组
(1)需求分析 1:
需要统计某公司 50 个员工的工资情况,例如计算平均工资、找到最高工资等。用之前
知识,首先需要声明 50 个变量来分别记录每位员工的工资,这样会很麻烦。因此我们可以
将所有的数据全部存储到一个容器中统一管理,并使用容器进行计算。
(2 )容器的概念:
生活中的容器:水杯(装水等液体),衣柜(装衣服等物品),集装箱(装货物
等)。
程序中的容器:将多个数据存储到一起,每个数据称为该容器的元素。
9.1.2 什么是数组
数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个标识符命名,
并通过编号(索引,亦称为下标或角标)的方式对这些数据进行统一管理。

9.1.4 数组的特点
(1)创建数组时会在内存中开辟一整块连续的空间,占据的空间的大小,取决于数组
的长度和数组中元素的类型。
(2)数组中的元素在内存中是依次紧密排列的且有序的。
(3)数组一旦初始化完成,其长度就是确定的,数组的长度一旦确定,就不能修改。
(4)我们可以直接通过索引(下标)获取指定位置的元素,速度很快。
9.2 数组的操作
9.2.1 数组的定义
(1)方式一:先指定元素的个数和类型,再进行初始化
//定义数组,数组名字是arr1,元素类型int,元素个数是3个
int arr1[3];
//定义完成后再给元素赋值
arr[0]=100;
arr[1]=200;
arr[2]=300;
(2)方式二:指定元素的类型和个数并同时进行初始化
//定义完数组直接进行初始化
int arr[3]={1,3,5};
(3)方式三:指定元素的类型,不指定元素个数,同时进行初始化
//没有指定元素个数,系统会自动计算
int arr[]={1,3,5,7,9};
9.2.2 访问数组元素
通过“数组名[下标]”可以访问数组中的元素,案例如下
#include<stdio.h>
int main()
{
int nums[4] = { 10,20,30,40 };
//修改第二个元素的值
nums[1] += 100;
//读取元素的值
printf("第一个元素的值:%d\n", nums[0]); // 第一个元素的值:10
printf("第二个元素的值:%d\n", nums[1]); // 第一个元素的值:120
printf("第三个元素的值:%d\n", nums[2]); // 第一个元素的值:30
printf("第四个元素的值:%d\n", nums[3]); // 第一个元素的值:40
return 0;
}
第一个元素的值:10
第二个元素的值:120
第三个元素的值:30
第四个元素的值:40
9.2.3 数组越界
数组下标必须在指定范围内使用,超出范围视为越界。

#include<stdio.h>
int main()
{
int nums[5] = { 10,20,30,40,50 };
printf("下标是 0 的元素:%d\n", nums[0]); // 10
printf("下标是 4 的元素:%d\n", nums[4]); // 50
printf("下标是-1 的元素:%d\n", nums[-1]);// 得到的是不确定结果
printf("下标是 5 的元素:%d\n", nums[5]); // 得到的是不确定结果
return 0;
}
下标是 0 的元素:10
下标是 4 的元素:50
下标是-1 的元素:-858993460
下标是 5 的元素:-858993460
9.2.4 计算数组的长度
数组长度(元素个数)是在数组定义时明确指定且固定的,我们不能在运行时直接获取
数组长度,但是,我们可以通过 sizeof 运算符间接计算出数组长度,计算步骤如下:
(1)使用 sizeof 运算符计算出整个数组的字节长度。
(2)由于数组成员是同一类型,每个元素字节长度相等,用整个数组的字节长度除以
单个元素的字节长度就可以得到数组的长度。

#include<stdio.h>
int main()
{
// 定义数组 没有指定长度
int nums[] = { 10,20,30,40,50,60,70 };
// 计算数组总的字节长度
int arrBytelen = sizeof nums;
// 用总字节长度除以单个元素的字节长度
int arrlen = arrBytelen / sizeof nums[0];
printf("数组的长度:%d", arrlen);
return 0;
}
数组的长度:7
9.2.5 遍历数组
遍历数组是指按顺序访问数组中的每个元素,以便读取或修改它们,编程中一般使用循
环结构对数组进行遍历。
#include<stdio.h>
int main()
{ //定义数组
int arr[10] = { 12,2,31,24,15,36,67,108,29,51 };
//计算数组长度
int len = sizeof arr / sizeof arr[0];
//遍历数组中的元素
printf("遍历数组中的元素:\n");
printf("%d\n", len);
for (int i = 0; i < len; i++)
{
printf("%d:%d\n", i, arr[i]);
}
return 0;
}
遍历数组中的元素:
10
0:12
1:2
2:31
3:24
4:15
5:36
6:67
7:108
8:29
9:51
我们还可以通过数组遍历完成数组的初始化赋值,看下面案例。
创建长度为 10 的数组,元素依次赋值为 0,1,2,3,4,5,6,7,8,9,并按逆序输
出每个元素,代码如下:
#include<stdio.h>
int main()
{
//声明数组
int arr[10];
//计算数组长度
int len = sizeof arr / sizeof arr[0];
//遍历数组进行初始化赋值,依次赋值0,1,2,3,4,5,6,7,8,9,
for (int i = 0; i < len; i++)
{
arr[i] = i;
}
//通过遍历逆顺序输出数组
for (int i = len-1; i >=0; i--)
{
printf("%d\n", arr[i]);
}
return 0;
}
9
8
7
6
5
4
3
2
1
0
9.2.6 数组应用案例
(1)案例一:计算数组中所有元素的和以及平均数。
#include<stdio.h>
int main()
{
//定义数组
int arr[10] = { 12,2,45,67,14,68,104,42,65,89 };
//计算数组长度
int len = sizeof arr / sizeof arr[0];
printf("数组长度为%", len);
int sum = 0;
for (int i = 0; i < len-1; i++)
{
sum += arr[i];
}
//计算平均值、
int avg = sum / len;
// 输出结果
printf("数组元素之和:%d\n", sum);
printf("数组元素平均值:%d", avg);
return 0;
}
数组长度为数组元素之和:419
数组元素平均值:41
(2)案例2:取出数组中之值最大的元素。
#include<stdio.h>
int main()
{
//定义一个数组
int arr[10] = { 14,42,23,54,5,66,74,58,79,60 };
//计算数组长度
int len = sizeof arr / sizeof arr[0];
printf("数组长度为:%d\n", len);
//定义变量用于存储最大元素值,初始值为第一个元素的值
int max = arr[0];
//遍历数组比较最大值
for (int i = 0; i < len; i++)
{
//如果当前遍历到元素比max大,就当前元素的值赋值给max
if (arr[i] > max)
{
max = arr[i];
}
}
printf("最大的元素值是:%d\n", max);
return 0;
}
数组长度为:10
最大的元素值是:79
9.3 字符数组
9.3.1 字符数组(字符串)介绍
用来存放字符的数组称为字符数组,也可以称为字符串。字符串的输入输出格式占位符
是 %s。

字符串结尾,会自动添加一个 \0 作为字符串结束的标志,所以字符数组最后一个元素必须是 \0
\0 是 ASCII 码表中的第 0 个字符,用 NUL 表示,称为空字符,该字符既不能显示,也
不是控制字符,输出该字符不会有任何效果,它在 C 语言中仅作为字符串的结束标志。
9.3.2 字符数组(字符串)的定义
(1)方式一:最后一个元素设置成\0
在给某个字符数组赋值时,赋值的元素个数小于字符数组的长度,则会自动在后面加
'\0', 表示字符串结束; 赋值的元素的个数等于该数组的长度(或不指定数组长度),则不
会自动添加 '\0'。
#include<stdio.h>
int main()
{
char str1[12] = { 'H','e','l','l','o',' ','w','o','r','l','d','\0' };
char str2[4] = { 't','o','m' };//后面会自动添加\0
char str3[] = { 'j','a','c','k' };//不会自动添加\0
printf("str1=%s \n", str1);
printf("str2=%s \n", str2);
printf("str3=%s \n", str3); // 由于没有结束标识,会包括相邻内存的数据,直到遇到结束标记
return 0;
}
str1=Hello world
str2=tom
str3=jack���������������������������������-␦�
(2)方式二:
#include<stdio.h>
int main()
{
char str1[] = { " I am happy" };//后面自动添加\0
char str2[] = "I am happy";//省略{}号,后面自动添加\0
printf("\n str1=%s", str1);
printf("\n str2=%s", str2);
return 0;
}
str1= I am happy
str2=I am happy
9.3.3.字符数组(字符串)的访问和遍历
字符数组(字符串)的访问和遍历,按照一般数组的方式访问和遍历即可。
#include<stdio.h>
int main()
{
//定义字符串
char greeting[] = "hello";
//计算字符串长度
int len = sizeof greeting / sizeof greeting[0];
printf("%s\n", greeting);
printf("数组长度:%d\n", len);
printf("第三个字符:%c\n", greeting[2]);
//遍历字符串
for (char i = 0; i < len; i++)
{
printf("%c\n", greeting[i]);
}
return 0;
}
hello
数组长度:6
第三个字符:l
h
e
l
l
o
9.4. 多维数组
9.4.1 多维数组介绍
如果数组的元素还是数组,这样的数组就称为多维数组。这种多层次的结构允许我们以
表格或矩阵的方式组织数据,其中每个维度都对应于不同的行、列或更多的维度,使数据更
加结构化和有组织。
多维数组可以分为二维数组、三维数组、四维数组 …… 等,这里我们以二维数组为例
进行演示。
下图是一个四行六列的二维数组示意图:

9.4.2 二维数组的定义
(1)方式一:先定义再初始化
//定义一个4行6列的二维数组
int a[4][6];
//进行初始化赋值
a[0][0] = 10;
a[0][1] = 20;
a[0][2] = 30;
a[0][3] = 40;
a[0][4] = 50;
a[0][5] = 60;
a[0][6] = 70;
a[0][7] = 80;
...
(2)方式2:直接定义并初始化
//定义一个4行6列的二维数组,以为矩阵的形式初始化
int a[4][6]={
{1001,2002,3003,4004,5005,6006},
{1010,2020,3030,4040,5050,6060},
{1100,2200,3300,4400,5500,6600},
{1111,2222,3333,4444,5555,6666}
};
//定义一个4行6列的二维数组,会自动匹配到各行各列
int b[4][6]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24};
//如果所赋值的数量可以与元素数量对应,第一维的数组长度可以不给出
int b[][6]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24};
9.4.3 二维数组的访问和遍历
访问二维数组的元素,需要使用两个下标(索引),一个用于访问行(第一维),另一
个用于访问列(第二维),我们通常称为行下标(行索引)或列下标(列索引)。
遍历二维数组,需要使用双层循环结构。
思路分析:

#include<stdio.h>
int main()
{
//定义一个3行4列的数组
int map[3][4] = {
{1,2,3,4},
{11,12,13,14},
{21,22,23,24}};
//计算第一维长的度
int rows = sizeof(map) / sizeof(map[0]);
//计算第二维的长度
int cols = sizeof(map[0]) / sizeof(int);
//遍历输出每个元素
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < rows; j++)
{
printf("%d\t", map[i][j]);//\t可以输出得更加整齐
}
printf("\n");
}
//计算所有元素的和
int sum = 0;
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
sum += map[i][j];
}
}
printf("所有元素的和:%d", sum);
}
1 2 3
11 12 13
21 22 23
所有元素的和:150
9.4.4 二维数组的内存分析
用矩阵形式(如 3 行 4 列形式)表示二维数组,是逻辑上的概念,能形象地表示出行列关系。而在内存中,各元素是连续存放的,不是二维的,是线性的。
C 语言中,二维数组中元素排列的顺序是按行存放的。即:先顺序存放第一行的元素,
再存放第二行的元素。
比如,举例,数组 a[3][4] 在内存中的存放:

·9.4.5 二维数组应用分析
现在有三个班,每个班五名同学,用二维数组保存他们的成绩,并求出每个班级平均分、
以及所有班级平均分,数据要求从控制台输入。
思路分析:
#include <stdio.h>
int main()
{
// 定义 一个 3 行 5 列的数组
double scores[3][5];
// 定义变量存储第一个维度长度和第二个维度长度
int rows = 3, cols = 5;
// 遍历二维数组进行赋值
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
printf("请输入第%d 个班的第%d 个学生的成绩:", i + 1, j + 1);
scanf("%lf", &scores[i][j]);
}
}
// 遍历数组,计算每个班级的平均分和总的平均分
// 定义变量记录所有班级总分数
double total_sum = 0;
// 定义变量记录每个班级总分数
double class_sum = 0;
// 遍历班级(行)
for (int i = 0; i < rows; i++)
{
// 将当前班级总分数重置为 0
class_sum = 0;
// 遍历当前班级的每个成绩(该行的每一列)
for (int j = 0; j < cols; j++)
{
class_sum += scores[i][j];
}
// 输出当前班级的平均分
printf("第%d 个班级的平均分为:%.2f \n", i + 1, class_sum / cols);
// 将该班级总分加入到所有总分中
total_sum += class_sum;
}
// 输出所有班级平均分
printf("所有班级的平均分为:%.2f", total_sum / (rows * cols));
return 0;
}
9.5 课后练习
(1)从终端循环输入 5 个成绩,保存到 double 数组,并输出。
(2)一个养鸡场有 6 只鸡,它们的体重分别是 3kg,5kg,1kg,3.4kg,2kg,50kg。请问这六只鸡的总体重是多少?平均体重是多少?
(3)创建一个 char 类型的 26 个元素的数组,分别放置'A'-'Z‘。使用 for 循环访问所有
元素并打印出来。
(4)请求出一个数组的最小值,并得到对应的索引。
第十章 函数
10.1 函数的基本概念
10.1.1 为什么需要函数
《街霸》游戏中,实现人物出拳、出脚或跳跃等动作都需要编写 50-80 行的代码,在每
次出拳、出脚或跳跃的地方都需要重复地编写这 50-80 行代码,这样程序会变得很臃肿,可
读性也非常差。为了解决代码重复编写的问题,可以将出拳、出脚或跳跃的代码提取出来放
在一个 {} 中,并为这段代码起个名字,这样在每次的出拳、出脚或跳跃的地方通过这个名
字来调用这个 {} 的代码就可以了。
提取出来的代码可以看作是程序中定义的一个函数,程序在需要出拳、出脚或跳跃时调
用该函数即可。
10.1.2 什么是函数
函数是一种可重复使用的代码块,用于执行特定的任务或操作。
函数允许我们将代码逻辑组织成独立的单元,从而提高了代码的可读性、可维护性和重
用性。
一个 C 程序可以由一个或多个源文件构成(C 文件扩展名是“.c”),一个源文件是一个
编译单位。一个源文件可以由若干个函数构成,函数之间可以相互调用。也就是说,函数是 C
程序基本的组成单位。

10.1.3 函数的作用
(1)封装功能,将一个完整的功能封装成函数,提高代码的结构化和复用性。
(2)代码模块化,将程序按照功能拆分成若干模块单元,有助于降低复杂度。
(3)增强可维护性,如果需要修改某项功能,只需要调整对应的函数代码。
(4)隔离细节,通过函数调用可以隐藏实现细节,只关心输入输出。
10.1.4 函数的分类
C 语言中,从使用的角度,函数可以分类两类。
(1)库函数,也称为标准函数,是由 C 系统提供的,用户不必自己定义,可直接使用
它们,使用库函数,必须包含 #include 对应的头文件。
(2)自定义函数,解决具体需求而自己定义的函数,需先定义再使用。
10.2 函数基本语法
10.2.1 声明函数语法
返回类型 函数名 (参数列表)
{
函数体语句1;
函数体语句2;
...
函数体语句n;
return 返回值;
}
(1)函数语法的组成部分

1)函数名:函数被调用时使用的名字,函数名要符合标识符规范。
2)函数体:函数中所包含的代码块,用于实现函数的具体功能和操作。
3)参数:用于接收调用函数时传递进来的值。
4)返回值:函数执行完毕后,从函数传回到调用点的值,返回值的类型要与函数名
前面的返回类型对应,如果没有返回值,返回类型可以写 void。
(2)代码示例:
#include<stdio.h>
//声明函数
void func()
{
printf("hello func\n");
}
//实现两个数字相减
int minus(int m, int n)
{
return m - n;
}
//取两个数字中的最大值
int max(int a, int b)
{
int c;
c = a > b ? a : b;
return 0;
}
//主函数
int main()
{
return 0;
}
(3)函数不能嵌套声明
C 程序中的所有函数都是互相独立的,一个函数并不从属于另一个函数,即函数不能嵌
套声明。
//错误演示
int func1(int a,int b) //第 1 个函数的定义
{
...
int func2(int c,int d) //第 2 个函数的定义
{
...
}
...
有些编译器的扩展允许函数嵌套声明,但这不是 C 标准的一部分,代码的可移植性可
能会受到影响,强烈不建议。
10.2.2 函数调用
函数名后面加上圆括号即是函数的调用,参数写在小括号中,函数每调用一次,函数体
语句都会执行一遍。
#include<stdio.h>
//声明函数
void func()
{
printf("hello func\n");
}
//实现两个数字相减
int minus(int x, int y)
{
return x - y;
}
//取两个数的最大值
int max(int a, int b)
{
int c;
c = a > b ? a : b;
return c;
}
//主函数
int main()
{
//函数调用
func();
func();
printf("17-90的结果:%d\n",minus(17,90));
printf("21-180 的结果:%d\n", minus(21, 180));
printf("12和16之间的最大值是:%d\n",max(12,16));
printf("45 和 31 之间较大的是:%d\n", max(45, 31));
return 0;
}
每日一测day-5
1. 【问答题】如何计算数组的长度
【答案】
(1)使用 sizeof 运算符计算出整个数组的字节长度。
(2)由于数组成员是同一类型,每个元素字节长度相等,用整个数组的字节长度除以单个
元素的字节长度就可以得到数组的长度。
【解析】
略。
2. 【问答题】字符数组(字符串)的最后一个元素是什么
【答案】
\0
【解析】
略。
3. 【代码题】请写出下面程序的运行结果
int arr[3][2] = {{10,20},{30,40},{50,60}};
printf("%d", arr[1][1] + arr[2][0]);
【答案】
90
【解析】
arr[1][1] 可以获取到 40,arr[2][0] 可以获取到 50, 40+50=90。
4. 【代码题】请写出下面程序的运行结果
int num = 100;
void func()
{
num += 20;
}
int main()
{
int num = 50;
func();
printf("%d", num);
return 0;
}
【答案】
50
【解析】
func 函数中使用的是全局变量 num,main() 函数中输出的是局部变量 num, 两者没
有关联。
5. 【代码题】请写出下面程序的运行结果
void func()
{
int n1 = 10;
static int n2 = 20;
n1 ++;
n2 ++;
printf("%d %d \n", n1, n2);
}
int main()
{
func();
func();
return 0;
}
【答案】
11 21
11 22
【解析】
(1)n1 是普通的局部变量,函数调用时创建,函数调用结束销毁,func() 调用两次,每次
都会创建新的变量 n1, 故两次 n1 的值都是 11。
(2)n2 是静态局部变量,只在函数第一次调用时创建,函数调用结束也不会销毁,所以第
二次调用 func() 的时候并没有创建新的 n2, 会继续在上次基础上累加,所以第一次值是
21, 第二次是 22。
6. 【代码题】请写出下面程序的运行结果
void func(int n)
{
printf("%d \n", n);
if (n > 1) {
func(n - 1);
}
printf("%d \n", n);
}
int main()
{
func(3);
return 0;
}
【答案】
3
2
1
1
2
3
【解析】
(1)func(3) 被调用,输出 3,然后进入递归调用 func(2)。
(2)func(2) 被调用,输出 2,然后进入递归调用 func(1)。
(3)func(1) 被调用,输出 1,然后不再进行递归调用,再次输出 1。
(4)返回到上一级的 func(2),输出 2。
(5)返回到最初的调用 func(3),输出 3

1379

被折叠的 条评论
为什么被折叠?



