目录
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>
#include<string.h>
#include<time.h>
#define MAX_ROW 3
#define MAX_COL 3
//1、创建棋盘并初始化
void Init(char chessBoard[MAX_ROW][MAX_COL])
{
#if 0
for (int row = 0; row < MAX_ROW; row++)
{
for (int col = 0; col < MAX_COL; col++)
{
chessBoard[row][col] = ' ';
}
}
#endif
srand((unsigned int)time(0));
memset(chessBoard, ' ', MAX_ROW * MAX_COL * sizeof(char));
}
//2、打印棋盘
void Print(char chessBoard[MAX_ROW][MAX_COL])
{
for (int row = 0; row < MAX_ROW; row++)
{
printf("+---+---+---+\n");
printf("| %c | %c | %c |\n", chessBoard[row][0], chessBoard[row][1], chessBoard[row][2]);
}
printf("+---+---+---+\n");
}
int menu()
{
printf("======================\n");
printf("欢迎来到三子棋游戏\n");
printf("1. 开始游戏\n");
printf("0. 退出游戏\n");
printf("======================\n");
printf("请输入您的选择: \n");
int choice = 0;
scanf("%d", &choice);
return choice;
}
//3.提示玩家落子(输入坐标)
void playerMove(char chessBoard[MAX_ROW][MAX_COL])
{
while (1)
{
printf("轮到玩家落子!\n");
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;
}
if (chessBoard[row][col] != ' ')
{
//当前位置已经有子了,不能重复落子
printf("该位置已经有子了!\n");
continue;
}
chessBoard[row][col] = 'X';
break;
}
}
//5、提示电脑落子——随机落子
void computerMove(char chessBoard[MAX_ROW][MAX_COL])
{
while (1)
{
printf("轮到电脑落子!\n");
int row = rand() % MAX_ROW;
int col = rand() % MAX_ROW;
if (chessBoard[row][col] != ' ')
{
continue;
}
chessBoard[row][col] = 'O';
break;
}
}
//判断是否满
int IsFull(char chessBoard[MAX_ROW][MAX_COL])
{
//查找下棋盘是否有空位
for (int row = 0; row < MAX_ROW; row++)
{
for (int col = 0; col < MAX_COL; col++)
{
if (chessBoard[row][col] == ' ')
{
//找到空位置了,说明棋盘没满
return 0;
}
}
}
//所有位置找完之后也没找到空格,最终才返回
return 1;
}
//4.检查游戏是否结束
//返回值表示游戏状况
//1、返回“X”表示玩家获胜
//2、返回“O”表示电脑获胜
//3、返回“ ”表示还未获胜
// 4、返回“Q”表示和局
char check(char chessBoard[MAX_ROW][MAX_COL])
{
//1.检查所有的行,看是否有连成3个子的情况
for (int row = 0; row < MAX_ROW; row++)
{
if (chessBoard[row][0] != ' '
&& chessBoard[row][0] == chessBoard[row][1]
&& chessBoard[row][0] == chessBoard[row][2])
{
return chessBoard[row][0];
}
}
//2、再检查所有的列,看是否有连成3个子的情况
for (int col = 0; col < MAX_COL; col++)
{
if (chessBoard[0][col] != ' '
&& chessBoard[0][col] == chessBoard[1][col]
&& chessBoard[0][col] == chessBoard[2][col])
{
return chessBoard[0][col];
}
}
//3、再检查对角线,看是否有连成3个子的情况
if (chessBoard[0][0] != ' '
&& chessBoard[0][0] == chessBoard[1][1]
&& chessBoard[0][0] == chessBoard[2][2])
{
return chessBoard[0][0];
}
if (chessBoard[0][2] != ' '
&& chessBoard[0][2] == chessBoard[1][1]
&& chessBoard[0][2] == chessBoard[2][0])
{
return chessBoard[0][2];
}
//4、判定是否和棋? 如果棋盘满了就算和棋
if (IsFull(chessBoard))
{
return 'Q';
}
return ' ';
}
void game()
{
#if 1
//一局游戏的核心函数
//1.创建棋盘并初始化棋盘 一个字符型的二维数组
char chessBoard[MAX_ROW][MAX_COL] = { 0 };
Init(chessBoard);
char winner = ' ';
while (1)
{
//2.打印棋盘
Print(chessBoard);
//3.提示玩家落子(输入坐标)
playerMove(chessBoard);
//4.检查游戏是否结束
winner = check(chessBoard);
if (winner != ' ')
{
break;
}
//5、提示电脑落子——随机落子
computerMove(chessBoard);
//6.检查游戏是否结束
winner = check(chessBoard);
if (winner != ' ')
{
break;
}
}
Print(chessBoard);
if (winner == 'X')
{
printf("恭喜你,您赢了\n");
}
else if (winner == 'O')
{
printf("你真菜,你连电脑都下不会");
}
else
{
printf("和棋");
}
}
#endif
int main()
{
#if 1
while (1)
{
int choice = menu();
if (choice == 1)
{
game();
}
else if (choice == 0)
{
printf("goodBye!\n");
break;
}
else
{
printf("您的输入有误,请重新输入:\n");
}
}
#endif
//打印的格式要匹配,否则程序会崩溃
//int a = 100;
//printf("%s\n", a);
return 0;
}
5. 数组的应用实例2:扫雷游戏
5.1 扫雷游戏思考
1.创建地图,9*9的二维数组,元素类型是什么?
2.每个格子的状态有以下状态
a)未翻开(草地)
b)已翻开(数字)
c)是地雷
d)不是地雷
四种状态是两大类,用两个二维数组表示。
第一个数组表示翻开状态showMap:
char类型表示,如果是 * 表示未翻开 ; 如果是阿拉伯数字,就表示翻开
第二个数组表示是否地雷状态mineMap:
char 类型表示 如果是 【1】:表示是地雷 【0】:表示不是地雷 、 也可以int 类型表示 如果是数字1:地雷; 数字0:不是地雷
5.2 扫雷游戏程序基本流程
程序基本流程:
- 先创建地图(两个地图都要创建),并进行初始化
a)针对showMap 初始化全是 *
b)针对mineMp 初始化10个地雷(根据随机位置来摆放) 没地雷用数字0 表示,地雷位置用数字1表示 - 打印地图showMap
- 提示玩家输入要翻开的位置坐标,并进行校验
- 判定当前位置是否是雷,如果是雷则游戏失败,游戏结束
- 如果当前位置是最后一个“不是雷的格子”那么游戏胜利,游戏结束
- 如果不是雷,更新翻开的当前位置,把【*】替换成一个数字(就把showMap中对应位置的 * 修改成一个具体的数字)
这个数字要根据当前位置周围8个格子的地雷数目来决定 - 下一步从2继续开始,直到游戏结束。
5.3 C语言函数的小bug:一个函数只能返回一个返回值
由于C语言函数不能直接返回两个值,所以我们只能通过参数的方式来返回,参数类型需要是指针类型,才能返回值。
这种做法是C语言中的一个常见做法,但是这个其实也是C语言中的无奈之举。
- 输出型参数:如果参数类型是指针类型,函数内部就能够对函数外部的变量产生影响此时这样的参数就称为“输出型参数
主要是C语言的函数只有一个返回值,
如果需要一个函数需要产生多个输出结果,只能通过输出型参数的方式来解决。更优雅的方案:一个返回值能返回多个值。C语言不支持,但是GO语言支持一个函数返回多个值的。(也支持输出性参数)
go语言是学习python语言的,Python 很早就实现了这个功能,就是一个函数返回多个返回值。
5.3 扫雷游戏代码具体实现
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#define MAX_ROW 9
#define MAX_COL 9
#define MINE_COUNT 10
int menu()
{
printf("=============================================\n");
printf("欢迎来到扫雷游戏!\n");
printf("==================1:游戏开始=================\n");
printf("==================0:游戏结束=================\n");
printf("=============================================\n");
printf("请输入您的选择:");
int choice = 0;
scanf("%d", &choice);
return choice;
}
void init(char showMap[MAX_ROW][MAX_COL], char mineMap[MAX_ROW][MAX_COL])
{
memset(showMap, '*', MAX_ROW * MAX_COL * sizeof(char));
srand((unsigned int)time(0));
memset(mineMap, '0', MAX_ROW * MAX_COL * sizeof(char));
int count = MINE_COUNT;
while (count > 0)
{
int row = rand() % MAX_ROW;
int col = rand() % MAX_COL;
if (mineMap[row][col] == '1')
{
//表明这个位置已经是雷了;重新随机
continue;
}
mineMap[row][col] = '1';
count--;
}
}
void Print(char showMap[MAX_ROW][MAX_COL])
{
printf(" | ");
for (int c = 0; c < MAX_COL; c++)
{
printf("%d ", c);
}
printf("\n");
printf("--+------------------+");
printf("\n");
for (int i = 0; i < MAX_ROW; i++)
{
printf("%d |", i);
for (int j = 0; j < MAX_COL; j++)
{
printf("%c ", showMap[i][j]);
}
printf("|\n");
}
printf("--+------------------+");
printf("\n");
}
//由于C语言函数不能直接返回两个值,所以我们只能通过参数的方式来返回,参数类型需要是指针类型,才能返回值。
//这里的row 和col这俩变量都是输出型参数 ,这俩参数在调用函数的时候并不需要给传递具体的值。
// 但是在函数返回的时候,就会把这两个值返回到row 和col这俩变量里面,相当于写到函数外面的变量了
//这种做法是C语言中的一个常见做法,但是这个其实也是C语言中的无奈之举,
//
//输出型参数:如果参数类型是指针类型,函数内部就能够对函数外部的变量产生影响
//此时这样的参数就称为“输出型参数”
//主要是C语言的函数只有一个返回值,如果需要一个函数需要产生多个输出结果,只能通过输出型参数的方式来解决
void Input(char showMap[MAX_ROW][MAX_COL], int* row, int* col )
{
while (1)
{
printf("请输入你要翻开的坐标(row,col):");
scanf("%d %d", row, col);//这俩参数已经是指针了,不需要& ,&就意味得到变量的指针
if (*row < 0 || *row >= MAX_ROW
|| *col < 0 || *col >= MAX_COL)
{
printf("您的输入有误,请重新输入:");
continue;
}
if (showMap[*row][*col] != '*') //这里也是一样的道理他是指针,需要使用*进行解引用
{
//说明当前位置已经被翻开。需要重新输入
printf("当前位置已经被翻开,请重新输入:");
continue;
}
break;
}
}
int checkMine(char mineMap[MAX_ROW][MAX_COL],
int row, int col)
{
if (mineMap[row][col] == '1')
{
return 1;
}
return 0;
}
int checkLastBlank(int* count)
{
//思路:记录当前翻开格子的数量。一共81个格子,有10个是雷,只要翻开71个不是雷的格子就游戏胜利
//这个count 的生命周期就是一局游戏的生命周期
*count += 1;
if (count == 71)
{
printf("恭喜你,游戏胜利!\n");
return 1;
}
else
{
return 0;
}
}
void Update(char showMap[MAX_ROW][MAX_COL],
char mineMap[MAX_ROW][MAX_COL],
int row, int col)
{
//翻开指定位置之后,要把这个位置的“*”替换成一个数字
//需要先统计这个位置周围8个格子中有几个雷,
int count = 0;
for (int r = row - 1; r <= row + 1; r++)
{
for (int c = col - 1; c <= col + 1; c++)
{
if (r < 0 || r >= MAX_ROW
|| c < 0 || c >= MAX_COL)
{
continue;
}
if (r == row && c == col)
{
continue;
}
if (mineMap[r][c] == '1')
{
count++;
}
}
showMap[row][col] = count + '0';
//showMap[row][col] = count + 48;
}
}
void game()
{
//1、先创建两个地图,并初始化
char showMap[MAX_ROW][MAX_COL] = { 0 };
char mineMap[MAX_ROW][MAX_COL] = { 0 };
init(showMap, mineMap);
int* count = 0;
while (1)
{
//2、打印地图
Print(mineMap);//这里为了我们测试方便,就顺便把地雷的地图打印出来方便我们看showMap的打印是否正确。最终游戏会删掉这句代码。
Print(showMap);
//3、提示玩家输入要翻开位置的坐标,并校验
int row = 0;
int col = 0;
Input(showMap, &row, &col);
//4、检查当前玩家翻开的位置是否是雷,如果是,则游戏失败
if (checkMine(mineMap, row, col))
{
Print(mineMap);
printf("你踩雷了,游戏失败\n");
break;
}
//5、检查玩家翻开的当前位置是否是最后一个位置,如果是,则游戏胜利
if (checkLastBlank(&count))
{
printf("恭喜你,扫雷成功!\n");
break;
}
//6、更新翻开的当前位置,把【*】替换为一个数字,数字根据周围八个地图的地雷数量决定。
Update(showMap, mineMap, row, col);
}
}
int main()
{
while (1)
{
int choice = menu();
if (choice == 1)
{
game();
}
else if (choice == 0)
{
printf("游戏结束!\n");
break;
}
else
{
printf("您的输入有误,请重新输入:\n");
continue;
}
}
return 0;
}
1033

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



