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~2
y = rand() % col; //0~2
if (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 10
void 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
原理同上。