前言
本章将对C语言的数组进行讲解,从一维数组开始讲起。已经学了三个章节了,所以本章还附加了三子棋和扫雷两个简单的小游戏,读者可以试着写一写,增加编程兴趣,提高模块化编程思想。
一、一维数组
0x00 何为数组
📚 数组,即为一组相同类型的元素的集合;
0x01 一维数组的创建
📚 数组的创建
① type_t:数组的元素类型;
② arr_name:数组名;
③ const_n:常量表达式,用于指定数组大小;
📌 注意事项
① 数组创建,[ ] 中要给定常量,不能使用变量;
② 数组 [ ] 中的内容如果不指定大小(不填),则需要初始化;
💬 一维数组创建方法演示
💬 const_n中要给定一个常量,不能使用变量
int main() { int count = 10; int arr[count]; // error return 0; } #define N 10 int main() { int arr2[N]; // yes return 0; }
0x02 一维数组的初始化
📚 初始化:在创建数组的同时给数组的内容置一些合理的初始值;
💬 初始化演示
int main() { int arr1[10]; // 创建一个大小为10的int类型数组 char arr2[20]; // 创建一个大小为20的char类型数组 float arr3[1]; // 创建一个大小为1的float类型数组 double arr4[] = {0}; // 创建一个不指定大小的double类型数组(需要初始化) return 0; }
💬 字符数组初始化
int main() { char ch1[5] = {'b', 'i', 't'}; char ch2[] = {'b', 'i', 't'}; char ch3[5] = "bit"; // 'b', 'i', 't', '\0', '0' char ch4[] = "bit"; // 'b', 'i', ''t, '\0' return 0; }
💬 字符数组初始化的两种写法
双引号写法自带斜杠0,花括号写法不自带斜杠0(需要手动添加)
int main() { char ch5[] = "bit"; // b, i, t, \0 【自带斜杠0】 char ch6[] = {'b', 'i', 't'}; // b i t 【不自带斜杠0】 printf("%s\n", ch5); printf("%s\n", ch6); return 0; }
没有 \0 时,strlen读取时并不会知道什么时候结束,strlen:遇到斜杠0就停止
int main() { char ch5[] = "bit"; // b, i, t, \0 【自带斜杠0】 char ch6[] = {'b', 'i', 't'}; // b i t 【不自带斜杠0】 printf("%d\n", strlen(ch5)); printf("%d\n", strlen(ch6)); return 0; }
🚩 >>> 3 随机值
💡 当然,你可以给他手动加上一个斜杠0,这样就不会是随机值了;
int main() { char ch5[] = "bit"; // b, i, t, \0 【自带斜杠0】 char ch6[] = {'b', 'i', 't', '\0'}; // b, i, t, + '\0' 【手动加上斜杠0】 printf("%d\n", strlen(ch5)); printf("%d\n", strlen(ch6)); return 0; }
🚩 >>> 3 3
0x03 一维数组的使用
📚 下标引用操作符: [ ] ,即数组访问操作符;
📚 数组的大小计算方法:整个数组的大小除以一个字母的大小
💬 打印一维数组
可以利用 for 循环,逐一打印数组
int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int sz = sizeof(arr) / sizeof(arr[0]); int i = 0; for(i = 0; i < sz; i++) printf("%d ", arr[i]); return 0; }
🚩 >>> 1 2 3 4 5 6 7 8 9 10
🔺 总结:
① 数组是使用下标来访问的,下标从0开始;
② 可以通过计算得到数组的大小;
0x04 一维数组在内存中的存储
📚 按地址的格式打印:%p (十六进制的打印)
💬 一维数组的存储方式
int main() { int arr[10] = {0}; int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); for(i = 0; i < sz; i++) printf("&arr[%d] = %p\n", i, &arr[i]); return 0; }
🚩 运行结果如下:
💡 仔细检视输出结果可知:随着数组下标的增长,元素的地址也在有规律的递增;
🔺 结论:数组在内存中时连续存放的;
二、二维数组
0x00 二维数组的创建
📚 二维数组 [行] [列]
① const_n1:行
② const_n2: 列
💬 二维数组的创建
int main() { int arr[3][4]; // 创建一个3行4列的int型二维数组; /* 0 0 0 0 0 0 0 0 0 0 0 0 */ char arr[3][5]; // 创建一个3行5列的char型二维数组; double arr[2][4]; // 创建一个2行4列的double型二维数组; return 0; }
0x01 二维数组的初始化
📚 初始化:在创建数组的同时给数组的内容置一些合理的初始值;
📌 注意事项:
① 二维数组初始化时,行可以省略,但是列不可以省略;
② 二维数组在内存中也是连续存放的;
💬 初始化演示
int main() { int arr[3][4] = {1,2,3,4,5}; /* 1 2 3 4 5 0 0 0 0 0 0 0 */ int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; // 完全初始化 int arr2[3][4] = {1,2,3,4,5,6,7}; // 不完全初始化 - 后面补0; int arr3[3][4] = {{1,2}, {3,4}, {4,5}}; // 指定; /* 1 2 0 0 3 4 0 0 4 5 0 0 */ return 0; }
💬 关于 " 行可以省略,列不可以省略 "
int main() { int arr1[][] = {{2,3}, {4,5}}; // error int arr2[3][] = {{2,3}, {4,5}}; // error int arr2[][4] = {{2,3}, {4,5}}; // √ return 0; }
0x03 二维数组的使用
💬 打印二维数组
同样是通过下标的方式,利用两个 for 循环打印
int main() { int i = 0; int j = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) printf("%d", arr4[i][j]); // 二维数组[行][列]; printf("\n"); // 换行; } }
💬 二维数组在内存中的存储
int main() { int arr[3][4]; int i = 0; int j = 0; for(i = 0; i < 3; i++) { for(j = 0; j < 4; j++) printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]); } return 0; }
🚩 运行结果如下:
💡 仔细检视输出结果,我们可以分析到其实二维数组在内存中也是连续存存放的;
🔺 结论:二维数组在内存中也是连续存放的;
三、数组作为函数参数
0x00 关于数组名
📚 数组名是首元素的地址(有两个例外)
⭕ 例外1:
sizeof(数组名) 计算的是整个数组的大小
💬 验证
int main() { int arr[10] = {0}; printf("%d\n", sizeof(arr)); return 0; }
🚩 >>> 40
⭕ 例外2:
& 数组名 表示整个数组,取出的是整个数组的地址
0x01 冒泡排序(Bubble Sort)
📚 冒泡排序核心思想:两两相邻元素进行比较,满足条件则交换;
① 先确认趟数;
② 写下一趟冒泡排序的过程;
③ 最后进行交换;
📌 注意事项:
① int arr [ ] 本质上是指针,int * arr ;
② 数组传参时,实际上传递的是数组的首元素地址;
③ sz 变量不能在 bubble_sort内部计算,需要在外部计算好再传递进去;
💬 冒泡排序:请编写一个bubble_sort ( ) 函数,升序,int arr[] = {9,8,7,6,5,4,3,2,1,0} ;
#include <stdio.h> void bubble_sort (int arr[], int sz) // 形参arr本质上是指针 int* arr { /* 确认趟数 */ int i = 0; for(i = 0; i < sz; i++) { /* 一趟冒泡排序干的活 */ int j = 0; for(j = 0; j <= (sz-1-i); j++) // -1:最后一趟不用排,-i:减去已经走过的趟 { /* 如果前面数比后面数大,就交换 */ if(arr[j] > arr[j + 1]) { /* 创建临时变量交换法 */ int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } } int main(void) { int arr[] = {9,8,7,6,5,4,3,2,1,0}; int sz = sizeof(arr) / sizeof(arr[0]); /* 冒泡排序 */ bubble_sort(arr, sz); // 数组传参的时候,传递的是首元素的地址 /* 打印数组 */ int i = 0; for(i=0; i<=sz; i++) printf("%d ", arr[i]); return (0); }
🚩 >>> 0 1 2 3 4 5 6 7 8 9 10
⚡ 算法优化:我们可以置一个变量来判断数组是否有序,如果已经有序,就不需要再冒泡排序了;
#include <stdio.h> void bubble_sort (int arr[], int sz) { int i = 0; for(i = 0; i < sz; i++) { int j = 0; int falg = 1; // 标记1,假设这一趟冒泡排序已经有序 for(j = 0; j <= (sz-1-i); j++) { if(arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; flag = 0; // 仍然不有序,标记为0 } } if(flag == 1) break; // 已经有序了,就不需要再冒泡排序了 } } int main(void) { int arr[] = {9,8,7,6,5,4,3,2,1,0}; int sz = sizeof(arr) / sizeof(arr[0]); /* 冒泡排序 */ bubble_sort(arr, sz); /* 打印数组 */ int i = 0; for(i=0; i<=sz; i++) printf("%d ", arr[i]); return (0); }