1、一维数组的创建和初始化
1.1、数组的创建
数组是一组相类型元素的集合。
数组的创建方式:
type_t arr_name [const_n];//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小。
1.2、数组的初始化
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。
//不完全初始化,剩余的元素默认初始化位0
int arr1[10] = {1,2,3};int arr2[] = {1,2,3};int arr3[5] = {'1','2','3','4','5'};char arr4[3] = {'a','98','c'};char arr5[10] = {'a','b','c'};char arr6[10] = "abcdef";
arr5和arr6存放元素的效果一样,但是有些细节还是不相同的:
arr5:是人为放进去:‘a’,‘b’,‘c’,然后后面补充0。
arr6:是认为放进去:a’,‘b’,‘c’,‘\0’,然后后面补充0。
那为什么说效果一样呢?因为’\0’的ASCII值为0,所以数组里面存放的是’\0’,其实是以它的ASCII值0存放的。
重点是:字符串有’\0’,而字符没有’\0’。
1.3、一维数组的使用
int arr[10] = {0}; //数组的不完全使用int sz = sizeof(arr) / sizeof(arr[0]); //计算数组元素的个数arr[i] //数组的访问。
总结:
- 数组是使用下标来访问的,下标是从0开始的。
- 数组的大小是可以通过计算得到的。
1.4、一维数组在内存中的存储
看代码:
#include <stdio.h>
int main()
{int i = 0;int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(arr[0]);for (i = 1; i <= 9; i++){printf("&arr[%d]:%p\n", i, &arr[i]);}return 0;
}
输出:
可以发现:每一个元素地址值相差为4,因为一个int类型的数据占据4个字节。
随着数组下标的增长,元素的地址,也有规律的递增。
得出结论:数组在内存中是来逆序存放的。
1.5、数组的类型
我们知道一个变量是有类型的,比如:
int a = 0;
char u = ‘a’;
那数组有没有类型呢?答案:有!
int arr[10] = {0};//int [10]就是arr数组的类型,[]里面的10,也是类型的一部分,不能省略。
【补充:】sizeof()和strlen的用法
#include <stdio.h>
#include <string.h>
int main()
{char str[] = "hello bit";printf("%d %d", sizeof(str), strlen(str));return 0;
}
输出:
分析:
- sizeof()是一个操作符,是用来计算变量、类型、数组等所占空间大小的。
- strlen()是一个库函数,是专门求字符串长度的,只能针对字符串,从参数给定的地址向后一直找’\0’或者说统计’\0’之前出现的字符个数。
首先:str数组中存放:h e l l o _ b i t \0
注意str数组中是存放了10个元素的,一定一定要想到’\0’也是个元素,它也是存放在str数组中的,他也算一个元素。
因此str数组中存放了10个元素,又因为全部都是char类型的,所以str数组大小一共10字节。因为sizeof()输出为10。
而strlen()是统计字符串个数的,并且只统计’\0’之前的字符个数。因为’\0’之前有9个元素,所以strlen()输出为9。
2、二维数组的创建和初始化
2.2、二维数组的创建
//数组创建
int arr[3][4]; //表示这个二维数组是3行4列的,列就是一行放几个元素,像一个表格一样。二维数组本质上就是多行的数组char arr[3][5];double arr[2][4];
2.3、二维数组的初始化
//3行4列,前四个元素放在第一行,中间四个元素放在第二行,最后四个元素放在第三行
int arr[3][4] = {1,2,3,4,2,3,4,5,3,4,5,6};//前四个元素放在第一行,中间四个元素放在第二行,最后两个元素放在第三行然后剩余的两个位置用0补充
int arr[3][4] = {1,2,3,4,2,3,4,5,3,4};//将1,2元素放在第一行剩余补0,0,将3,4元素放在第二行剩余补0,0,将4,5元素放在第三行剩余补0,0
int arr[3][4] = {{1,2},{3,4},{4,5}};//只能省略行,不能省略列
int arr[][4] = {{1,2,3,4},{1,2}};int arr[][4] = {1,2,3,4,5,6};
2.3、二维数组的使用
行下标是从0开始,列下标也是从0开始,先确定行,在确定列。
#include <stdio.h>
int main()
{int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 4; j++){printf("%d ", arr[i][j]); //打印元素scanf("%d", &arr[i][j]); //输入元素}printf("\n");}return 0;
}
输出:
补充:如何把二维数组看作为一维数组
我们可以把二维数组的每一行看作是一维数组中的元素,然后把arr[0],arr[1],arr[2]看作是每一行的数组名。
2.4、二维数组在内存中的存储
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 4; j++){printf("arr[%d][%d]:%p\n", i, j, arr[i][j]);}}return 0;
}
输出:
可以发现其实二维数组和一维数组在空间中存储的形式是一样的,都是连续的空间。
//以存储空间的角度来看,以下是等价的:
int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 }; = int arr[12] = {1,2,3,4,2,3,4,5,3,4,5,6}
3、数组越界
数组的下标是有范围限制的。
数组的下标规定是从0开始,如果数组有n个元素,最后一个元素的下标就是n-1。
所以数组的下标如果是小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
C语言本身是不做数组下标的越界检查,编译器也不一定会报错,但是编译器不报错,并不意味着程序就是正确的。
所以程序员写代码时,最好自己做好越界的检查。
4、数组作为函数参数
往往我们写代码的时候,会将数组作为一个参数传给函数,比如:我要实现一个冒泡排序(这里要讲算法思想)函数。
将一个整型数组排序。
4.1、冒泡排序函数的错误设计
5、冒泡排序的核心思想
冒泡排序的核心思想:两个相邻的元素进行比较。
6、数组名
6.1、一维数组的数组名理解
我们通常说数组名就是数组首元素地址
#include <stdio.h>
int main()
{int arr[3] = { 1,2,3 };printf("%p\n", arr);printf("%p\n", &arr[0]);return 0;
}
输出:
可以看到地址确实是一样的。
所以得出结论:数组名确实能表示数组首元素的地址。
但是又两个例外:
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
除此以上两种情况,其它遇到的数组名都是数组首元素地址。
6.2、二维数组的数组名理解
二维数组的数组名也表示数组首元素地址。
比如:int arr[3][4]; 数组首元素地址表示的是第一行元素的地址。并不是第一行第一个的元素的地址。
7、计算二维数组的行数和列数
#include <stdio.h>
int main()
{int arr[3][4] = { 0 };//sizeof(arr)表示整个二维数组的大小,sizeof(arr[0])表示二维数组的第一行大小。printf("%d\n", sizeof(arr) / sizeof(arr[0]));//sizeof(arr[0])表示二维数组的第一行大小,sizeof(arr[0][0])表示二维数组的一个元素大小。printf("%d\n", sizeof(arr[0]) / sizeof(arr[0][0]));return 0;
}
8、三子棋
-
新建个项目
-
创建test.c 测试游戏的逻辑
-
创建game.c 游戏代码的实现
-
game.h 游戏代码的声明(函数的声明、符号定义)
test.c文件
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void menu()
{printf("*****************************\n");printf("******1. 进入游戏 0. 退出****\n");printf("*****************************\n");
}void game()
{char ret = 0;char board[ROW][COL] = { 0 };//初始化棋盘的函数InitBoard(board, ROW, COL);//打印棋盘DisplayBoard(board, ROW, COL);//下棋while (1){//玩家下棋PlayerMove(board,ROW,COL);//判断输赢ret = IsWin(board,ROW,COL);if (ret != 'C'){break;}DisplayBoard(board, ROW, COL);//电脑下棋ComputeMove(board,ROW,COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}DisplayBoard(board, ROW, COL);}if (ret == '*'){printf("玩家赢\n");DisplayBoard(board, ROW, COL);}else if (ret == '#'){printf("电脑赢\n");DisplayBoard(board, ROW, COL);}else{printf("平局\n");DisplayBoard(board, ROW, COL);}
}int main()
{srand((unsigned int)time(NULL));//设置随机数的生成起点int input = 0;do{menu();printf("请选择>");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("退出游戏\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}
game.c文件
#include "game.h"//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){board[i][j] = ' ';}}
}第一个版本
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
// int i = 0;
// for (i = 0; i < row; i++)
// {
// 打印数据
// printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
// 打印分割信息
// if (i < row - 1)
// {
// printf("---|---|---\n");
// }
// }
//}//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++){int j = 0;//打印数据for (j = 0; j < col; j++){printf(" %c ", board[i][j]);if (j < col - 1){printf("|");}}printf("\n");//打印分割信息if (i < row - 1){for (j = 0; j < col; j++){printf("---");if (j < col - 1){printf("|");}}printf("\n");}}
}//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{int x = 0;int y = 0;while (1){printf("玩家下棋\n");printf("请输入坐标:>");scanf("%d %d", &x, &y);//判断坐标是否合法if (x >= 1 && x <= row && y >= 1 && y <= col){//判断坐标是否被占用if (board[x - 1][y - 1] == ' '){board[x - 1][y - 1] = '*';break;}else{printf("坐标被占用,不能下棋,请选择其它位置\n");}}else{printf("坐标不合法,请重新输入:>\n");}}
}//电脑下棋
//找还未下棋的位置随机下棋
void ComputeMove(char board[ROW][COL], int row, int col)
{printf("电脑下棋:>\n");int x = 0;int y = 0;while (1){x = rand() % row; //0~2y = rand() % col; //0~2if (board[x][y] == ' '){board[x][y] = '#';break;}}
}//平局的函数,返回1表示填满了,返回0表示未填满
int IsFull(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){if (board[i][j] == ' '){return 0;}}}return 1;
}//判断输赢
char IsWin(char board[ROW][COL], int row, int col)
{//判断在行中赢的int i = 0;for (i = 0; i < row; i++){if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' '){return board[i][1];}}//判断在列中赢的int j = 0;for (j = 0; j < col; j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' '){return board[1][j];}}//判断对角线赢的if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' '){return board[1][1];}if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' '){return board[1][1];}//平局:都没赢就需要平局,平局的判断标准就是:以上赢的标准都不符合且九个格子全部填满。if (IsFull(board,ROW,COL)){return 'Q';}//游戏继续return 'C';
}
game.h文件
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define ROW 3
#define COL 3
#include <stdlib.h>
#include <time.h>//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);//电脑下棋
void ComputeMove(char board[ROW][COL], int row, int col);//判断输赢,包含四种情况:
//玩家赢-'*'
//电脑赢-'#'
//平局-'Q'
//继续-'C'
char IsWin(char board[ROW][COL], int row, int col);
9、扫雷
test.c文件
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"void menu()
{printf("*********************************\n");printf("********* 1.玩游戏 *********\n");printf("********* 0.退 出 *********\n");printf("*********************************\n");
}void game()
{//存放布置好雷的信息char mine[ROWS][COLS] = { 0 };//存放排查出雷的信息char show[ROWS][COLS] = { 0 };//mine 初始化数组在没有布置雷的时候都是'0'InitBoard(mine, ROWS, COLS,'0');//show 初始化数组在没有布置雷的时候都是'*'InitBoard(show, ROWS, COLS, '*');//设置雷SetMine(mine, ROW, COL);//打印棋盘,这里只需要打印9*9的棋盘,所以参数需要变化//DisplayBoard(mine, ROW, COL);DisplayBoard(show, ROW, COL);//排查雷FindMine(mine, show, ROW, COL);}int main()
{int input = 0;srand((unsigned int)time(NULL));do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:game();break;case 2:printf("退出游戏");break;default:printf("选择错误\n");break;}} while (input);return 0;
}
game.c
#include "game.h"//初始化棋盘为'0'
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{int i = 0;int j = 0;for (i = 0; i < rows; i++){for (j = 0; j < cols; j++){board[i][j] = set;}}
}//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{int i = 0;int j = 0;printf("-----------------扫雷游戏--------------------\n");for (j = 0; j <= col; j++){printf("%d ", j);}printf("\n");for (i = 1; i <= row; i++){printf("%d ", i); //添加行号for (j = 1; j <= col; j++){printf("%c ",board[i][j]);}printf("\n");}printf("-----------------扫雷游戏--------------------\n");
}//设置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{int count = EASY_COUNT;while (count){int x = rand() % row + 1;int y = rand() % col + 1;if (board[x][y] == '0'){board[x][y] = '1';count--;}}
}int get_mine_count(char board[ROWS][COLS],int x,int y)
{return (board[x - 1][y] +board[x - 1][y] +board[x][y - 1] +board[x + 1][y - 1] +board[x + 1][y] +board[x + 1][y + 1] +board[x][y + 1] +board[x - 1][y + 1] - 8 * '0'); }//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int win = 0; //记录非雷的个数while (win < row*col-EASY_COUNT){printf("请输入要排查的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){if (show[x][y] != '*'){printf("该坐标被排查过了,不能重复排查\n");}else{if (mine[x][y] == '1'){printf("很遗憾,你被炸死了\n");DisplayBoard(mine, ROW, COL);break;}else //如果不是雷,需要统计周围有几个雷{win++;int count = get_mine_count(mine, x, y);show[x][y] = count + '0'; //数字1转为字符'1'DisplayBoard(show, ROW, COL);}}}else{printf("输入非法坐标,请重新输入\n");}}if (win == row * col - EASY_COUNT){printf("恭喜你,排雷成功了\n");DisplayBoard(mine, ROW, COL);}
}
game.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2#define EASY_COUNT 10void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);void DisplayBoard(char board[ROWS][COLS], int row, int col);void SetMine(char board[ROWS][COLS], int row, int col);void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
'1’和1的运算
‘1’ - ‘0’ = 1
原理:'1’的ASCII值为49
'0’的ASCII值为48
‘0’ - ‘0’ = 0
原理同上。