【c语言】数组

目录

1.一维数组的创建和初始化

1.1 数组的创建

 1.2数组的初始化

1.3 数组的使用

1.4 一维数组在内存中的存储

2.二维数组的创建和初始化

2.1 二维数组的创建

2.2 二维数组的初始化

2.3 二维数组的使用

2.4 二维数组在内存中的存储

3.数组的越界

4.数组作为函数参数

4.1 冒泡排序

4.2冒泡排序函数的错误设计

4.3 数组名是什么

4.4 正确的冒泡排序函数 

5.实例

5.1 三子棋游戏

5.2 扫雷游戏


1.一维数组的创建和初始化

1.1 数组的创建

数组是一组相同类型元素的集合。 
#include <stdio.h>
int main() {
	//数组的创建
	int arr1[5] = {0};
	//大小为常量表达式
	int arr[2 + 3];
	
	//注意:C99之前只能用常量表示数组大小,C99之后引用的变长数组的概念,但是VS2022,2013不支持C99的变长数组
	/*int n = 0;
	scanf("%d", &n);
	int arr3[n];*/
	return 0;
}

使用VS Code连接远程Linux验证变长数组:

 1.2数组的初始化

//数组的初始化
int main() {
	//不完全初始化,剩下的元素默认初始化为0
	int arr[10] = { 1,2,3 };

	//完全初始化
	int arr1[3] = { 1,2,3 };

	//未指定大小的初始化(数组初始化后可以不指定大小,其大小根据初始化的内容来确定)
	int arr2[] = { 1,2,3 };

	//字符数组的初始化
	char arr3[] = { 'a',97,'c' };//b的ASCII码值为97,没有'\0'

	//用字符串初始化数组
	char arr4[] = "abc";//有'\0'
	return 0;
}

1.3 数组的使用

[] ,下标引用操作符。它其实就数组访问的操作符
1. 数组是使用下标来访问的,下标是从0开始。
2. 数组的大小可以通过计算得到。
#include <stdio.h>
//一维数组的使用
int main() {
	int arr[10] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++) {
		scanf("%d", &arr[i]);
	}
	for (i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	return 0;
}

1.4 一维数组在内存中的存储

//一维数组在内存中的存储
int main() {
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++) {
		arr[i] = 10 - i;
	}
	for (i = 0; i < sz; i++) {
		printf("&arr[%d]=%p\n", i,&arr[i]);
	}
	return 0;
}

数组在内存中是连续存放的且地址由低到高(通过数组的首元素地址即可访问数组)

2.二维数组的创建和初始化

2.1 二维数组的创建

//数组创建——添加了行和列的概念
int arr[3][4];
char arr[3][5];
double arr[2][4];

2.2 二维数组的初始化

//二维数组的初始化
int main() {
	//不完全初始化
	int arr[2][3] = { 0 };
	
	//完全初始化
	int arr1[2][3] = { 1,2,3,4,5,6 };

	//用{}将每一行的元素扩起来时,每一行可以不完全初始化
	int arr[2][3] = { {1},{3,4,5} };

	return 0;
}

注意:二维数组初始化后,数组的行可以不指定大小,但是列必须指定大小,否则下一行的元素的起始位置不确定(二维数组的内存存储)

2.3 二维数组的使用

#include <stdio.h>
//二维数组的使用
int main() {
	int arr[2][3] = { 0 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 2; i++) {
		for (j = 0; j < 3; j++) {
			//输入1-6的值
			arr[i][j] = i * 3 + j + 1;
		}
	}
	for (i = 0; i < 2; i++) {
		for (j = 0; j < 3; j++) {
			printf("%d ", arr[i][j]);
		}
	}
	return 0;
}

2.4 二维数组在内存中的存储

//二维数组在内存中的存储
int main() {
	int arr[3][3] = { {1,3},{2,4,5},{1,5,7} };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 3; j++) {
			printf("&arr[%d][%d]=%p\n", i, j, &arr[i][j]);
		}
	}
	return 0;
}

可以发现二维数组的行和列是便于我们理解的想象,不同行的元素在内存中是连续存放的,且地址由低到高变化

理解:二维数组实际上是一维数组的数组(数组名:arr[1],arr[2]...)

3.数组的越界

数组的下标是有范围限制的。
数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。
所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以程序员写代码时,最好自己做越界的检查
数组本身没有越界检查,编译器也不一定会报错越界访问:
一维数组,二维数组行和列都有可能越界

4.数组作为函数参数

4.1 冒泡排序

#include <stdio.h>
//冒泡排序:将相邻的两个数进行比较,得到升序的数组
//如果有n元素,则最多比较n-1趟;第一趟需要比较n-1对;之后每一趟递减1
int main() {
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
	//冒泡排序
	int i = 0;
	int j = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz - 1; i++) {
		//比较
		for (j = 0; j < sz - 1 - i; j++) {
			if (arr[j] > arr[j + 1]) {
				//交换
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
	for (i = 0; i < sz ; i++) {
		printf("%d ", arr[i]);
	}
	return 0;
}

4.2冒泡排序函数的错误设计

#include <stdio.h>
//冒泡排序的错误设计
//sort——排序
void sort(int arr[]) {
	//冒泡排序
	int i = 0;
	int j = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz - 1; i++) {
		//比较
		for (j = 0; j < sz - 1 - i; j++) {
			if (arr[j] > arr[j + 1]) {
				//交换
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}
int main() {
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
	sort(arr);
	for (int i = 0; i < 10; i++) {
		printf("%d ", arr[i]);
	}
	return 0;
}

 运行发现数组并没有升序:

进行调试发现:sz=1;i<sz-1,不进入循环;

我们可以猜想,数组作为函数参数的时候是否没有把整个数组的传递过去?

4.3 数组名是什么

数组名就是地址,通常来说,数组名就是数组首元素的地址,但是有两个例外:

1.sizeof(arr)——计算的是整个数组的大小

2.&arr——取出的是整个数组的起始地址,与首元素的地址相同,但是意义不同

&arr+1是在整个数组上+1

除此之外所有的数组名都是首元素的地址

4.4 正确的冒泡排序函数 

数组传参,形参可以是指针变量,也可以是整型数组

#include <stdio.h>
//正确的冒泡排序函数
//void sort(int arr[], int sz) {
void sort(int* arr,int sz){
	//冒泡排序
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++) {
		//比较
		for (j = 0; j < sz - 1 - i; j++) {
			if (arr[j] > arr[j + 1]) {
				//交换
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}
int main() {
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	sort(arr,sz);
	//注意:数组传参是传首元素的地址,本质上传的是指针变量,可以用int*来接收;
	for (int i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	return 0;
}

注意:实参不可以用&arr(数组的地址),无论是谁的地址,大小都是4字节,数组传参时一定要将数组的元素个数也传进去

5.实例

5.1 三子棋游戏

game.h文件

#pragma once
#include <stdio.h>
//注意:预处理指令不以分号结尾;
#define Row 3
#define Col 3

//函数声明:
//初始化棋盘
//#define 定义标识符常量Row,Col,不可以作为形参变量
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 ComputerMove(char board[Row][Col], int row, int col);

//判断输赢
char IsWin(char board[Row][Col], int row, int col);

game.c文件

#define _CRT_SECURE_NO_WARNINGS 1 
#include "game.h"
//初始化棋盘
void InitBoard(char board[Row][Col], int row, int col) {
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			board[i][j] = ' ';
		}
	}
}
//打印棋盘
void DisplayBoard(char board[Row][Col], int row, int col) {
	for (int i = 0; i < row; i++) {
		//打印每一行的列
		//printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);//只适用于三列棋盘
		//适用于所有棋盘
		for (int j = 0; j < col; j++) {
			printf(" %c ", board[i][j]);
			if (j < col - 1) {
				printf("|");
			}
		}
		//注意:数据行和分割行的换行操作
		//注意:printf()函数输出需要打印的字符串,必须用双引号打印,尽管只有一个字符
		printf("\n");
		if (i < row - 1) {
			for (int j = 0; j < col; j++) {
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

//玩家下棋
void PlayerMove(char board[Row][Col], int row, int col) {
	//注意:每一次输出都要换行
	printf("玩家下棋\n");
	int x = 0;
	int y = 0;
	while (1) {
		printf("请输入坐标:\n");
		scanf("%d %d", &x, &y);
		if ((x < 1) || (x > row) || (y < 1) || (y > col)) {
			printf("非法输入,请重新输入\n");
		}
		else if (board[x - 1][y - 1] != ' ') {
			printf("该坐标被占用,请重新输入\n");
		}
		else {
			board[x - 1][y - 1] = '*';
			break;
		}
	}
}

//电脑下棋
void ComputerMove(char board[Row][Col], int row, int col) {
	printf("电脑下棋\n");
	int x = 0;
	int y = 0;
	while (1) {
		//随机生成坐标
		int x = rand() % row;
		int y = rand() % col;
		//注意:玩家的坐标与电脑随机生成的坐标的区别
		if (board[x][y] == ' ') {
			board[x][y] = '#';
			break;
		}
	}
}

//判断平局
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;
}

//判断输赢——直接返回棋盘中的数据,可以省去判断
//四种情况
//玩家赢——'*',电脑赢——'#',平局——'Q',游戏继续——'C'
//只要三点连成一线即可赢,这里的输赢判断只局限于三子棋
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][0] != ' ') {
			return board[i][0];
		}
	}
	for (i = 0; i < col; i++) {
		 if (board[0][i] == board[1][i] && board[1][i] == board[2][1] && board[0][i] != ' ') {
			return board[0][i];
		}
	}

	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') {
		return board[0][0];
		}
	if (IsFull(board, Row, Col)) {
		return 'Q';
	}
	else
		return 'C';
}

test.c文件

#include "game.h"
//三子棋设计
void menu() {
	printf("*****************************\n");
	printf("********* 1.play ************\n");
	printf("********* 0.exit ************\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);
		DisplayBoard(board, Row, Col);
		//判断输赢
		ret = IsWin(board, Row, Col);
		if (ret != 'C') {
			break;
		}

		//电脑下棋
		ComputerMove(board, Row, Col);
		DisplayBoard(board, Row, Col);
		//判断输赢
		ret = IsWin(board, Row, Col);
		if (ret != 'C') {
			break;
		}
	}
	if (ret == '*') {
		printf("玩家赢\n");
	}
	else if (ret == '#') {
		printf("电脑赢\n");
	}
	else {
		printf("平局\n");
	}
}
int main() {
	int input = 0;
	//调用一次srand函数
	srand((unsigned int)time(NULL));
	do {
		menu();
		printf("请选择(0/1):\n");
		scanf("%d", &input);
		switch (input) {
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("非法输入,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

5.2 扫雷游戏

扫雷:

1.不是雷则显示周围八个位置雷的个数;

2.是雷则游戏结束;

3.将所有不是雷的位置找出来则游戏结束


注意:假设用1和0表示雷和非雷,则当显示雷的个数为1时会有歧义——用两个数组分别表示布置雷和显示雷
注意:当检查边缘位置雷的个数时可能会越界,因此需要对每一个需要检查的位置进行判断是否越界——可以将数组board[n][n]初始化的范围扩大为board[n+2][n+2]

test.c文件

#define _CRT_SECURE_NO_WARNINGS 1 
#include "game.h"
void menu() {
	printf("*********************************\n");
	printf("************ 1.play *************\n");
	printf("************ 0.exit *************\n");
	printf("*********************************\n");
}
void game() {
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	InitBoard(mine, ROWS, COLS,'0');
	InitBoard(show, ROWS, COLS,'*');
	SetMine(mine, ROW, COL);
	//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("请选择:\n");
		scanf("%d", &input);
		switch (input) {
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("非法选择,请重新输入\n");
		}
	} while (input);
	return 0;
}

game.h文件

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

#define MINE_COUNT 10

void InitBoard(char Board[ROWS][COLS], int rows, int cols,char set);

void SetMine(char mine[ROWS][COLS], int row, int col);

void DisplayBoard(char Board[ROWS][COLS], int row, int col);

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS] , int row, int col);

game.c

#define _CRT_SECURE_NO_WARNINGS 1 
#include "game.h"
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 SetMine(char mine[ROWS][COLS], int row, int col) {
	int count = MINE_COUNT;
	while (count) {
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0') {
			mine[x][y] = '1';
			count--;
		}
	}
	
}

void DisplayBoard(char Board[ROWS][COLS], int row, int col) {
	int i = 0;
	int j = 0;
    system("cls");
	printf("——————————————扫雷——————————————\n");
	for (i = 0; i <= col; i++) {
		printf("%d ", i);
	}
	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");
}

static int Get_MineCount(char mine[ROWS][COLS],int x, int y) {
	return mine[x - 1][y - 1] + 
		mine[x - 1][y] + 
		mine[x - 1][y + 1] + 
		mine[x][y - 1] + 
		mine[x][y + 1] + 
		mine[x + 1][y - 1] + 
		mine[x + 1][y] + 
		mine[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-MINE_COUNT) {
		printf("请输入要检查的位置:\n");
		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 {
				int c = Get_MineCount(mine, x, y);
				show[x][y] = c + '0';
				win++;
				DisplayBoard(show, row, col);
			}
		}
		else
			printf("非法输入,请重新输入坐标\n");
	}
	if (win == ROW * COL - MINE_COUNT) {
		printf("恭喜你,排雷结束!\n");
	}
}

注意:打印周围雷的个数可以打印并查看字符'1'的个数,也可以计算周围位置的字符和-8*'0'得到雷的个数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值