10天学会嵌入式技术之C语言-day-5

第九章 数组

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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值