目录
1.一维数组
1.1的创建和初始化。
数组是一组相同类型元素的集合。
1.1.1 创建数组
int arr[ 4 ] = {1,2,3,4};
注意:
- [4]中的4是可以省略的.数组的长度就和后面初始化的列表里的元素个数一致~~
- [4]如果不省略的时候,后面的初始化列表的元素不能比4个多~~
- [4]如果不省略的时候, ,后面的初始化列表的元素可以比4个少的时候,就把数组剩下的元素填成0.
- 创建数组的时候, []里面的数字只能是一个常量, 不能是变量(在C89中)
- 如果省略[4] 中的数字,就必须使用{ }初始化~~
- 如果没有省略[4],并且也没有初始化~会显示一个随机值,这个值是VS自己填充的(16进制下是0xcc,翻译成中文是“烫烫烫”)0XCC:是CPU的一个特殊指令——int3中断指令.为了防止数组越界,设置的一个固定值
如果将int arr[4]放在main函数外面,这个数组就表示是一个全局变量了
如果是一个全局变量,没有被显式初始化,会被自动初始化为0.
如果是一个局部变量,没有被显式初始化,就是.上次残留的值~~
只要创建变量都应该显式初始化~~
1.1.2 数组初始化
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。针对字符数组初始化,可以像普通数组一样来使用{ }进行初始化,也可以使用“ ”用一个字符串。
char str1[] = { 'a','b','c' };
char str2[] = { 'a',98,'c' };
char str2[] = "abc";//注意:写双引号的时候,就是字符串,多了一个‘\0’
//不是每个字符数组都可以称为“字符串”
//字符串是一种特殊的字符数组,一定是以'\0'结尾(“ ”)!
strlen(str1);//这是一个未定义行为,!!!因为不是所有数组都是字符串数组,只有字符串数组结尾才是‘\0’,才能这么求长度
不是每个字符数组都可以称为“字符串”,字符串是一种特殊的字符数组,一定是以’\0’结尾(“ ”)!
给任何str系列的函数(比如strlen, strcmp…)进行传参的时候,必须要由人来保证参数是一个合法的字符串~
1.2 一维数组的使用
数组的使用:
- 求数组长度:sizeof(arr)/siziof(arr[0])
- 取下标 :[ ]
0开始,到len-1结束
10 [ 0, 9] 一旦数组下标越界,就是未定义行为!!
#include<stdio.h>
//创建数组
int main()
{
int arr[10] = { 0 };
//以循环的方式,给这个数组设置一个值,从1到10
for (int i = 0; i < 10; i++)
{
arr[i] = i + 1;
}
for (int i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
1.3 一维数组在内存中的存储
#include<stdio.h>
//数组在内存中的存储
int main()
{
char arr[10] = { 0 };
for (int i = 0; i < 10; i++)
{
printf("%p\n", &arr[i]);
}
return 0;
}
数组中的元素处在连续的内存空间上~~
2. 二维数组
二维数组本质上也是一维数组,只不过每个元素又是一个一维数组,所谓的数组,可以理解成一种特殊的类型,int arr[4];可以理解成是创建了4个int 类型的变量,也可以理解成创建了一个变量arr,其类型是int [4]
啥叫三维数组?
本质上也是一个-维数组, 只不过每个元素又是一个二 维数组~~
啥叫四维数组?
不管是几维数组,本质都是一维数组. 操作/语法都是和一维数组一 样的
2.1 二维数组的创建和初始化
如何看,可以借助VS编译器来看
1.第一种
#include<stdio.h>
//数组在内存中的存储
int main()
{
//二维数组
int arr[3][4] = { 0 };
printf("啦啦啦");
return 0;
}
2.第二种
#include<stdio.h>
//数组在内存中的存储
int main()
{
//二维数组
int arr[3][4] = { 0 };
int arr1[3][4] = { 1,2,3,4,5 };
printf("啦啦啦");
return 0;
}
3.第三种
int arr3[3][4] = {
{1,2},
{3,4},
{5}
};
{1,2,0,0}
{3,4,0,0}
{5,0,0,0}
4.第四种
int arr4[3][4] = {
(1,2),
(3,4),
(5)
};
这里的(1,2)涉及到逗号表达式
逗号表达式:a,b, C =>的值就是c,即最后一个逗号后面的值.
所以这种二维数组初始化内容为:
{2,4,5,0}
{0,0,0,0}
{0,0,0,0}
5.第五种
int arr5[][4] = {
1,2,3
};
前面的可以省略
这个数组初始化为{1,2,3,0}
针对二维数组,列数一定要存在,不能省略,对于三维、四维、五维,都是只有第一个方括号可以省略不写,其他均不能省略
2.2. 二维数组的使用
#include<stdio.h>
//二维数组的使用
int main()
{
int arr[3][4] = {
2,4,5
};
for (int row = 0; row < 3; row++)
{
// arr[row] 这个操作相当于取出其中的某一行,这一行也是一个数组
for (int col = 0; col < 4; col++)
{
printf("%d ", arr[row][col]);//这里会先去取第一个下标,然后再取第二个下标
}
printf("\n");
}
return 0;
}
2.3 二维数组在内存中的存储
像一维数组一样,这里我们尝试打印二维数组的每个元素。它也和一维数组一样是顺序存储的
#include<stdio.h>
//数组在内存中的存储
int main()
{
int arr[3][4] = {
2,4,5
};
for (int row = 0; row < 3; row++)
{
// arr[row] 这个操作相当于取出其中的某一行,这一行也是一个数组
for (int col = 0; col < 4; col++)
{
printf("%p ", &arr[row][col]);//这里会先去取第一个下标,然后再取第二个下标
}
printf("\n");
}
return 0;
}
3. 数组作为函数参数
C语言中的第二个惊天BUG:数组名变成指针
数组名作为函数的参数也会触发转成指针的操作.在函数内部是无法直接求数组的长度的.必须要在函数外面提前把长度求好,再以参数的形式传给函数内部
3.1 实现冒泡排序
冒泡排序的核心思路,比较两个相邻元素的大小,看看是不是符合排序的要求.(例如,要求进行升序排序,就看前一个元素是不是比后一个元素小)如果不符合排序要求,就进行交换~
从前往后遍历,每次取出一个最大的元素放到数组最后了;从后往前遍历,每次取出一个最小的元素放到数组最前了~~
#include<stdio.h>
//冒泡排序
int bubbleSort(int arr[], int size)
{
//写一个从后往前遍历的版本,
//每次从后往前遍历比较相邻元素,每一趟比较把最小的元素放在最前面
//bound => 边界的意思,使用这个变量的值,表示已排序区间和待排序区间的分界线
//[0,bound ) 表示已排序区间
//[bound,size)表示待排序区间
int bound = 0;
for (; bound < size; bound++)
{
//通过这个循环,来及控制后续比较相邻元素的比较趟数
//接下来实现一趟比较交换的过程
for (int cur = size - 1; cur > bound;cur --)//size-1表示最后一个元素的下标。可以通过arr[size-1]来获取到最后一个元素
{//进行比较
//相邻元素就是以cur为基准
//cur - 1 , cur + 1;
//由于我们现在cur 是从 size -1 开始的 所以我们不能使用cur +1,cur+1的话可能数组下标越界,产生未定义行为
//所以我们使用cur -1来表示相邻元素
if (arr[cur - 1] > arr[cur])
{//不符合升序条件,就进行交换
int tem = arr[cur - 1];
arr[cur - 1] = arr[cur];
arr[cur] = tem;
}
}
}
}
int main()
{
int arr[] = { 2,5,1,6,7,8,10 };
int size = sizeof(arr) / sizeof(arr[0]);
bubbleSort(arr, size);
for (int i = 0; i < size; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
写代码的过程中,经常会遇到这类问题:
到底是+1还是-1 ,还是不加不减,到底是<还是<=。"差一问题"实际开发中,非常容易出错的问题。也是测试_工程师在设计测试用例的时候,重点关注的地方。解决思路:把边界值具体代入数值,推演程序是否符合要求
自己写的代码
在这里插入代码片//自己实现一下冒泡排序~先实现一下老师讲课版本(从前往后遍历)
int bubble(int arr[], int size)
{//从后往前遍历
int bound = 0;
for (; bound < size; bound++)
{
for (int cur = size - 1; cur > bound; cur--)
{
if (arr[cur - 1] > arr[cur]) {
int tem = arr[cur - 1];
arr[cur - 1] = arr[cur ];
arr[cur] = tem;
}
}
}
}
再写一个从前往后遍历版本;
int bubble2(int arr[], int size)
{
int bound = size-1;
for (; bound > 0; bound--)
{
for (int cur = 0; cur <= bound; cur++)
{
if (arr[cur] < arr[cur+1])
{
int tem = arr[cur ];
arr[cur ] = arr[cur+1];
arr[cur+1] = tem;
}
}
}
return 0;
}
4. 数组的应用实例1:三子棋
只要连成一条线就赢了。
- 我们先表示棋盘:创建一个 3*3的二维数组,每个元素是一个char类型,'x’表示玩家1、'o’表示玩家2、'空格’表示空白。
- 游戏流程:
(1)创建棋盘,并初始化~把所有位置都设为空格
(2)打印棋盘
(3)玩家进行落子,让玩家来输入一组坐标(row,col),进行落子
(4)判定是否获胜
(5)电脑进行落子~ 随机落子
先想清楚,程序该咋写,再动手,然后写成上面这种条理性的描述,然后在动手敲代码
//课堂代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
//三子棋
#define MAX_ROW 3
#define MAX_COL 3
//这里的 3 称为 magic number 魔幻数字~~不建议直接写进去!建议创建两个宏
void init(char chess[MAX_ROW][MAX_COL]) {
for (int row = 0; row < MAX_ROW; row++) {
for (int col = 0; col < MAX_COL; col++) {
chess[row][col] = ' ';
}
}
}
void print(char chess[MAX_ROW][MAX_COL]) {
printf("+---+---+---+\n");
for (int row = 0; row < MAX_ROW; row++) {
printf("|");
for (int col = 0; col < MAX_COL; col++) {
printf(" %c |", chess[row][col]);
}
printf("\n+---+---+---+\n");
}
}
void playerMove(char chess[MAX_ROW][MAX_COL]) {
printf("玩家落子......\n");
while (1)
{
printf("请输入 坐标(row col):");
int row = 0;
int col = 0;
scanf("%d %d", &row, &col);
//进行合法性校验,判定用户输入的内容是否合法
//如果不合法,就让用户重新输入
if (row < 0 || row >= MAX_ROW|| col < 0 || col >= MAX_COL){
//不合法,就让用户重新输入
printf("您的输入有误!请重新输入!\n");
continue;//结束本次循环,进行下一次循环
}
// 假设用户输入了 1, 1 但是 1, 1 位置已经有子了
if (chess[row][col] != ' ') {
printf("您输入的位置已经有棋了!请重新输入!\n");
continue;
}
//进行落子
chess[row][col] = 'x';
break;
}
}
void computerMove(char chess[MAX_ROW][MAX_COL]) {
while (1) {
int row = rand() % MAX_ROW; //结果是 0 1 2
int col = rand() % MAX_COL;
if (chess[row][col] != ' ') {
//说明这个位置已经有子了
continue;
}
chess[row][col] = 'o';
break;
}
}
//返回 1 表示满了,返回 0 表示未满
int isFull(char chess[MAX_ROW][MAX_COL]) {
//遍历棋盘,看有没有空格,如果有,则棋盘未满
for (int row = 0; row < MAX_ROW; row++) {
for (int col = 0; col < MAX_COL; col++) {
if (chess[row][col] == ' ') {
return 0;
}
}
}
return 1;
}
//此处函数给一个char 返回值 我们约定
// 如果返回 x 表示玩家 获胜
// 如果返回 o 表示玩家 获胜
// 如果返回 ' ' 表示胜负未分,还要继续下棋
// 如果返回 p ,表示平局
char isGameOver(char chess[MAX_ROW][MAX_COL]) {
//扫描所有的行,所有的列,以及两个对角线
//(1)扫描所有的行,
for (int row = 0; row < MAX_ROW; row++) {
if (chess[row][0] != ' '
&& chess[row][0] == chess[row][1]
&& chess[row][0] == chess[row][2])
{
return chess[row][0];//return chess[row][1];
//return chess[row][2]; 均可,这三个是等价的
}
}
//(2)扫描所有列
for (int col = 0; col < MAX_COL; col++) {
if (chess[0][col] != ' '
&& chess[0][col] == chess[1][col]
&& chess[0][col] == chess[2][col])
{
return chess[0][col];//return chess[row][1];
//return chess[row][2]; 均可,这三个是等价的
}
}
// (3) 扫描两个对角线
if (chess[0][0] != ' '
&& chess[0][0] == chess[1][1]
&& chess[0][0] == chess [2][2]) {
return chess[0][0];
}
if (chess[0][2] != ' '
&& chess[0][2] == chess[1][1]
&& chess[0][2] == chess[2][0]) {
return chess[0][2];
}
//判定是否和棋
if (isFull(chess)) {
printf("p");
}
//胜负未分
return ' ';
}
int main()
{
//不建议使用全局变量,最好使用局部变量
char chessBoard[MAX_ROW][MAX_COL];
// 1. 对棋盘进行初始化
init(chessBoard);
char winner = isGameOver(chessBoard);
while (1)
{
// 2. 打印棋盘
print(chessBoard);
// 3. 玩家落子
playerMove(chessBoard);
// 4.判定胜负
if (winner != ' ') {
//游戏结束
break;
}
// 5. 电脑落子
computerMove(chessBoard);
// 6.判定胜负
char winner = isGameOver(chessBoard);
if (winner != ' ') {
//游戏结束
break;
}
}
if (winner == 'x')
{
printf("玩家方获胜!恭喜你赢了\n");
}
else if (winner == 'o') {
printf("电脑方获胜!你咋连人工智障都下不过!\n");
}
else {
printf("和棋了!你和人工智障五五开\n");
}
// 7. 打印棋盘
print(chessBoard);
return 0;
}