C语言:第08天笔记
内容提要
-
数组
-
排序算法:冒泡排序
-
二维数组
-
字符数组
-
数组
冒泡排序
-
排序思想(向前冒泡):
-
一次只排好一个数,针对n个数,最差情况需要n-1次就可以排好
-
每次排序假定第一个元素是最大或者最小的,用第一个元素的后面的元素一一与第一个元素比较,遇到较大或者较小的和第一个元素交换,访问完数组的最后一个元素,就排好了一个数。
-
在余下的数中,在次应用第2步的操作,直到只剩下1个数。
-
动图演示:
-
-
推理:
例如:将
5,4,3,2,1
冒泡排序为1,2,3,4,5
排序演示:
-
第0轮:5,4,3,2,1 → 4,3,2,1,5 比较4次 = 数组长度5 - 轮数0 - 1
-
第1轮:4,3,2,1,5 → 3,2,1,4,==5 ==比较3次 = 数组长度5 - 轮数1 - 1
-
第2轮:3,2,1,4,5 → 2,1,3,4,5 比较2次 = 数组长度5 - 轮数2 - 1
-
第3轮:2,1,3,4,5 → 1,2,3,4,5 比较1次 = 数组长度5 - 轮数3 - 1
总结
-
案例涉及到5个数的排序,排序了4轮,得到:轮数 = 元素个数(数组长度) - 1,我们可以
通过一个外层for循环实现轮数的遍历。
-
案例涉及的每一轮中数列的排序次数,计算规则:次数 = 元素个数 - 轮数 - 1,我们可以通
过一个内层for循环实现每一轮次数的遍历。
-
每一次比较过程中,两个数涉及到位置交换,比如a = 3, b = 4,交换ab的数据变为 a = 4,b =
3,应该如何实现:
-
引入一个临时变量temp,将a的值赋值给temp,int temp = a;
-
将b的值赋值给a,a = b;
-
将temp的值赋值给a,a = temp;
-
-
-
衍生:
冒泡排序 → 鸡尾酒排序 → 摇床排序
二维数组
定义
二维数组本质上是一个行列式组合,也就是说二维数组由行和列两部分组成。属于多维数组,二维数组数据通过行列进行解读。
二维数组可被视为一个特殊的一维数组,相当于二维数组又是一个特殊的一维数组,只不过它的元素是一维数组。(数组的元素的类型可以是数组类型)
语法
数据类型 数组名[行数][列数]
行数:外层数组的数组容量
列数:内层数组的数组容量
说明
-
二维数值在初始化的时候可以省略行数,系统会通过初始化后的数据自动推断行数。
-
二维数组和一维数组一样,也可以部分初始化,未初始化的数据默认使用
0
或者\0
对应的ASCII是0
补齐
举例
int arr[3][3] = {{11,12,13},{21,22,23},{31,32,33}};//等价于 int arr[][3] = {{11,12,13},{21,22,23},{31,32,33}}; //二维数组初始化可以省略行号 int arr[3][3] = {{11,12},{21,22,23},{31}} //正确 int arr[3][3] = {{11,12,0},{21,22,23},{31,0,0}} //正确 int arr[3][3] = {0}; //所有位置用0补齐 int arr[3][3] = {}; //所有位置用0补齐 int arr[3][3] = {11}; //除了0行0列用11,其余都用0补齐 int arr[][] = {{11,12,13},{21,22,23},{31,32,33}}; // 错误 int arr[3][] = {{11,12,13},{21,22,23},{31,32,33}}; //错误
注意:在C语言中,二维数组在计算机的存储顺序是按行进行的,即第一维的(行)下标变化慢,第二维的(列)下标变化快。
应用场合
主要是应用于对行列有要求的情况,比如 我们现在要存储西安粤嵌所有在班学生的成绩。
还有就是字符数组的应用,比如用数组存储学生的姓名。
特殊写法
-
下标可以是整型表达式,如:
a[2-1][2*2-1] → a[1][3]
-
下标可以是已经有值的变量或数组元素,如:
a[2*x-1][b[3][1]]
-
数组元素可以出现在表达式中,如:
b[1][2] = a[2][3]/2
注意:使用数组元素的下标应在已定义数组的大小范围内,应注意区别定义数组大小和引用数组元素的区别。
案例
案例1
-
需求:二维数组的遍历
-
分析:
-
二维数组的遍历需要使用双层的for循环,外层控制行,内层控制列
-
取数据:
arr[行号][列数]
-
-
代码
/************************************************************************* > File Name: demo02.c > Author: > Description: > Created Time: 2025年03月12日 星期三 11时17分17秒 ************************************************************************/ #include <stdio.h> int main(int argc,char *argv[]) { //创建一个二维数组 int arr[][3] = {{11},{21,22},{31,32,33}} //获取行数组容量和列数组容量 int row = sizeof(arr) /sizeof(arr[0][0]); int col = sizeof(arr[0]) / sizeof(arr[0][0]); //遍历数组 //外层循环控制行 for (int i = 0; i < row; i++) { //内层循环控制列(可以计算列的大小) //int col = sizeof(arr[i]) / sizeof(arr[i][0]) for(int j = 0; j < col; j++) { //输出元素 printf("%-3d",arr[i][j]); } } printf("\n"); return 0; }
案例2
-
需求:矩阵的转置
-
分析:
-
所谓的转置,就是原本的列变行,行变列
-
-
代码
/************************************************************************* > File Name: demo03.c > Author: > Description: > Created Time: 2025年03月12日 星期三 11时21分53秒 ************************************************************************/ #include <stdio.h> #define ROW 2 #define COL 3 int main(int argc,char *argv[]) { // int i,j; //创建数组 int arr_before[ROW][COL] = {{11,12,13},{21,22,23}}; int arr_after[COL][ROW] = {0}; //计算数组大小 int arr_before_row = sizeof(arr_before) / sizeof(arr_before[0]); int arr_before_col = sizeof(arr_before[0]) / sizeof(arr_before[0][0]); int arr_after_row = sizeof(arr_after) / sizeof(arr_after[0]); int arr_after_col = sizeof(arr_after[0]) / sizeof(arr_after[0][0]); //通过循环实现数组转置 for(i = 0; i < arr_before_row; i++) { for(j = 0; j < arr_before_col; j++) { //输出前 printf("%-4d",arr_before[i][j]); //转置后 arr_after[j][i] = arr_before[i][j]; } printf("\n"); } printf("\n"); printf("转置后\n"); for(i = 0; i < arr_after_row; i++) { for(j = 0; j < arr_after_col; j++) { printf("%-4d",arr_after[i][j]); } printf("\n"); } printf("\n"); return 0; }
扩展练习
-
需求:求一个3行3列的矩阵对角线上元素之和。
-
总结:等行等列的矩阵,转置前后,对角线上的数据相等
字符数组
在C语言中,支持常量字符串,不支持变量字符串,如果想要实现类似的变量字符串,C语言提供了两种
-
字符数组
char name[] = "哪吒";
-
字符指针
char *name = "哪吒"
概念
元素类型为char字符型的数组,字符数组往往是用来存储字符串数据的。需要注意的是,我们C语言中的字符是字节字符。
测试题:
char a = 'A'; //正确 char b = '1'; //正确 char c = 65; //正确,ASCII码 char d = "A"; //错误,char字符不能使用双引号 char e = '王'; //错误,因为一个中文汉字占2个字节
语法
//一维数组 char 数组名[数组容量]; //二维数组 char 数组名[行容量][列容量];
字符数组的语法就是我们前面所学的一维数组和二维数组的语法,只不过数据类型时char而已。
注意:
如果我们的char数组初始化的时候,没有完全初始化值的时候,使用‘\0’进行填充,注意,这里的‘\0’只是起到一个占位或者标识的作用,无法通过printf打印输出。
比如:
char c[9] = {'h','e','l','l','o'}; //等价于下面 char c[9] = {'h','e','l','l','o','\0','\0','\0'};
案例
案例1
-
需求:
-
输出一个字符序列(I LOVE YOU)
-
代码:
/************************************************************************* > File Name: demo04.c > Author: > Description: > Created Time: 2025年03月12日 星期三 14时32分42秒 ************************************************************************/ #include <stdio.h> int main(int argc,char *argv[]) { //创建一个数组,存储I LOVE YOU ASCII中对应空格:' ',其对应的ASCII值为32 char arr[] = {'I',' ','L','O','V','E',32,'Y','O','U'}; //计算数组大小 int len = sizeof(arr) / sizeof(arr[0]); //通过for循环遍历数组 for(int i = 0; i < len; i++){ printf("%c",arr[i]); } printf("\n"); return 0; }
案例2
-
需求:空心菱形
-
代码:
/************************************************************************* > File Name: demo05.c > Author: > Description: > Created Time: 2025年03月12日 星期三 14时45分46秒 ************************************************************************/ #include <stdio.h> int main(int argc,char *argv[]) { char arr[5][5] = { {' ',' ','*',' ',' '}, {' ','*',' ','*',' '}, {'*',' ',' ',' ','*'}, {' ','*',' ','*',' '}, {' ',' ','*',' ',' '} }; //计算 int row = sizeof(arr) / sizeof(arr[0]); int col = sizeof(arr[0]) / sizeof(arr[0][0]); // for(int i = 0; i < row; i++){ for(int j = 0; j < col; j++){ printf("%c",arr[i][j]); } printf("\n"); } printf("\n"); return 0; }
注意:
①如果定义时,不初始化,元素值不确定(指针定义在函数中的数组)
char arr1[2]; char arr2[5] = {'a','b','c'};//剩余\0填充②如果提供的字符个数大于数组长度,则按照语法错误处理(会报警告,但是编译通过);如果字符个数小于数组长度,后面的元素自动补\0
char arr1[2] = {'h','e','l'}; //会报警告,但是编译通过 char arr2[3] = {'a'}; //正确,后面的元素自动补\0③如果提供的字符个数与数组长度相同,可以省略数组长度,系统会自动确定元素个数,适合字符较多时。
char arr1[] = {'b','u'}; //正确
字符串结束标志
说明
-
C语言规定,字符串以字符
\0
作为结束标志 -
编译系统对字符串常量自动加一个
\0
作为结束标志,比如“hello”实际上存储{'h','e','l','l','o'} -
程序中往往通过判断
\0
来检测字符串是否结束。 -
\0
的ASCII码是0,不是一个可显示可输出的字符,是“空操作符”,它什么都不做,不会增加有效字符,仅仅用作一个工程判别的标志或者在字符数组中占位。char a[] = {'h','i'}; char a[] = {'h','i','\0'}; char b[] = "hello"; //实际为hello\0
字符数组的多样表示
我们的char数组可以以数组的形式一个一个输出,也可以以字符串的形式整体输出
案例:
/************************************************************************* > File Name: demo06.c > Author: > Description: > Created Time: 2025年03月12日 星期三 15时31分38秒 ************************************************************************/ #include <stdio.h> int main(int argc,char *argv[]) { //输入 char s1[] = {'h','e','l','l','o',' ','w','o','r','l','d','\0'}; char s2[] = {"hello world"}; char s3[] = "hello world"; //输出 for(int i =0; i < sizeof(s3) / sizeof(s3[0]); i++){ //过滤\0 if(s1[i] == '\0' || s2[i] == '\0' || s3[i] == '\0') continue; printf("%c,%c,%c\n",s1[i],s2[i],s3[i]); } printf("\n"); // printf("%s,%s,%s\n",s1,s2,s3); printf("\n"); return 0; }
注释:
-
字符串的长度与字符数组的长度不一定相同。
char c[] = {'h','\0','i','\0'}; //c作为数组,长度为:4 //c作为字符串,长度为:1
-
利用字符串常量可以对字符数组进行初始化,但不能用字符串常量对字符数组赋值。
//正确 char arr1[6] = "hello"; //错误 char arr2[6]; arr2 = "hello";
字符串的基础操作
在用格式化说明符%s进行输入输出时,其输入输出项均为数组名,但在输入时,相邻
两个字符串之间要用空格分隔,系统将自动在字符串后加上\0
,在输出时,遇到\0
结束
对于字符串的操作,需要使用系统提供的API函数
字符串输入
scanf
语法:
scanf("%s",数组名);
注意:数组名对应的数组只能是char类型
注意:采用scanf进行字符串输入,要求字符串中不能有空格,否则字符串遇到空格就会结束
fgets
语法:
fgets(数组名,数组容量,stdin);
功能:
从键盘录入一个字符串常量到字符数组,返回字符数组的地址(首地址,默认返回的地址,一般用12位16进制数表示)
说明:
采用fgets进行字符串输入,可获取所有输入的字符串,包含\0,在实际的字符串处理时,我们可能需要手动处理\0
注意:
①如果输入的字符串不包括空格或换行,可以使用scanf或者fgets
②如果输入的字符串不包括空格或换行,可以使用fgets
字符串输出
printf
语法:
printf("%s",数组名);
fputs
语法:
fputs(数组名,stdout);
功能:
输出一个字符串
说明:
字符串可以包含转移字符(可以识别)
案例:
printf("您的名字是"); fputs(a,stdout);
关于字符的输入:
scanf("%c","地址")
getchar();
关于单字符的输出:
printf("%c",....)
putchar();