C.C语言数组

文章目录

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

1.1. 数组的创建

1.2. 数组的初始化

1.3. 一维数组的使用

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

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

2.1. 二维数组的创建

2.2. 二维数组的初始化

2.3. 二维数组的使用

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

三. 数组越界

四. 数组作为函数参数

4.1. 冒泡排序函数的错误设计

4.2. 数组名是什么

4.3. 冒泡排序函数的正确设计

五. 数组练习

5.1. 数组应用实例1:三子棋

5.2. 数组应用实例2:扫雷


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

1.1. 数组的创建

数组是一组相同类型元素的集合,数组的创建方式:

type_t arr_name [const_n];
​
//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小

数组的创建方式:

//代码1
int arr1[10];
​
//代码2
int count = 10;
int arr2[count];//数组时候可以正常创建?
​
//代码3
char arr3[10]; float arr4[1]; double arr5[20];

注意:数组创建,在C99标准之前, [] 中要给一个常量才可以,不能使用变量。在C99标准支持了变长数 组的概念。

1.2. 数组的初始化

数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。

int arr1[10] = {1,2,3};
int arr2[] = {1,2,3,4};
int arr3[5] = {1,2,3,4,5}; 
char arr4[3] = {'a',98, 'c'}; 
char arr5[] = {'a','b','c'}; 
char arr6[] = "abcdef";

数组在创建的时候如果想不指定数组的确定的大小就得初始化。数组的元素个数根据初始化的内容来确定,但是对于下面的代码要区分,内存中如何分配。

char arr1[] = "abc";
char arr2[3] = {'a','b','c'};

1.3. 一维数组的使用

对于数组的使用我们之前介绍了一个操作符: [] ,下标引用操作符。它其实就数组访问的操作符,我们来看代码:

#include <stdio.h>
int main() 
{
  int arr[10] = {0};//数组的不完全初始化 //计算数组的元素个数
  int sz = sizeof(arr)/sizeof(arr[0]); 
  //对数组内容赋值,数组是使用下标来访问的,下标从0开始。所以: 
  int i = 0;//做下标
  for(i=0; i<10; i++)//这里写10,好不好? 
  {
    arr[i] = i; 
  }
    //输出数组的内容 
  for(i=0; i<10; ++i) 
  {
        printf("%d ", arr[i]);
    }
    return 0; 
}

总结:

  • 数组是使用下标来访问的,下标是从0开始。

  • 数组的大小可以通过计算得到。

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

接下来我们探讨数组在内存中的存储,看代码:

#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)
    {
        printf("&arr[%d] = %p\n", i, &arr[i]);
    }
    return 0; 
}

输出的结果如下:

                                                                       

仔细观察输出的结果,我们知道,随着数组下标的增长,元素的地址,也在有规律的递增。 由此可以得出结论:数组在内存中是连续存放的。

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

2.1. 二维数组的创建

// 数组创建
int arr[3][4];
char arr[3][5];
double arr[2][4];

2.2. 二维数组的初始化

// 数组初始化
int arr[3][4] = {1,2,3,4};
int arr[3][4] = {{1,2},{4,5}};
int arr[][4] = {{2,3},{4,5}}; // 二维数组如果有初始化,行可以省略,列不能省略

2.3. 二维数组的使用

二维数组的使用也是通过下标的方式,看代码:

#include <stdio.h>
int main()
{
	int arr[3][4] = { 0 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			arr[i][j] = i * 4 + j;
		}
	}
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
	}
	return 0;
}

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

#include <stdio.h>
int main()
{
	int arr[3][4];
	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;
}

                                                                        

通过结果我们可以分析到,其实二维数组在内存中也是连续存储的。

三. 数组越界

  • 数组的下标是有范围限制的。

  • 数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。

  • 所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。

  • C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就 是正确的, 所以程序员写代码时,最好自己做越界的检查。

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		printf("%d\n", arr[i]);//当i等于10的时候,越界访问了
	}
	return 0;
}

四. 数组作为函数参数

往往我们在写代码的时候,会将数组作为参数传个函数,比如:我要实现一个冒泡排序(这里要讲算法 思想)函数将一个整形数组排序。

4.1. 冒泡排序函数的错误设计

//方法1:
#include <stdio.h>
void bubble_sort(int arr[])
{
	int sz = sizeof(arr) / sizeof(arr[0]);// 这样对吗!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
	bubble_sort(arr);//是否可以正常排序?
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

方法1,出问题,那我们找一下问题,调试之后可以看到 bubble_sort 函数内部的 sz ,是1。 难道数组作为函数参数的时候,不是把整个数组的传递过去?

4.2. 数组名是什么

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	printf("%d\n", *arr);
	//输出结果
	return 0;
}

结论:数组名是数组首元素的地址(有两个例外),如果数组名是首元素地址,那么:

int arr[10] = {0};
printf("%d\n", sizeof(arr));

// 为什么输出的结果是:40?

补充

  • sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数 组。

  • &数组名,取出的是数组的地址。&数组名,数组名表示整个数组。

除此1,2两种情况之外,所有的数组名都表示数组首元素的地址。

4.3. 冒泡排序函数的正确设计

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

/// <summary>
/// <函数> 交换两个数 </函数>
/// </summary>
/// <param name="left"> 左边值 </param>
/// <param name="right"> 右边值 </param>
void swap(int* left, int* right) {
	int tmp = *left;
	*left = *right;
	*right = tmp;
}

/// <summary>
/// <函数> 冒泡排序 </函数>
/// </summary>
/// <param name="arr"> 数组数据区 </param>
/// <param name="size"> 数组数据个数 </param>
void bubble_sort(int arr[], int size) {  	// 在外围计算大小传入
	for (int i = 0; i < size - 1; i++) {
		bool flag = true;

		for (int j = 0; j < size - 1 - i; j++) {
			if (arr[j] > arr[j + 1]) {
				swap(&arr[j], &arr[j + 1]);
				flag = false;
			}
		}

		if (flag == true) {
			break;
		}
	}
}

/// <summary>
/// <函数> 打印数组中的内容 </函数>
/// </summary>
/// <param name="arr"> 数组数据区 </param>
/// <param name="size"> 数组数据个数 </param>
void print(int arr[], int size) {	// 在外围计算大小传入
	for (int i = 0; i < size; i++) {
		printf("%d ", arr[i]);
	}	
	printf("\n");
}

/// <summary>
/// <函数> 程序入口 </函数>
/// </summary>
/// <param name="argc"></param>
/// <param name="argv"></param>
/// <returns></returns>
int main(int argc, char* argv) {
	int arr[] = { 12,42,44,13,65,23,76,34 };
	int size = sizeof(arr) / sizeof(arr[0]);

	print(arr, size);
	bubble_sort(arr, size);
	print(arr, size);

	return 0;
}

五. 数组练习

5.1. 数组应用实例1:三子棋

  • 文件> game.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <time.h>

// <宏定义> 标识棋盘行数
#define ROW 3

// <宏定义> 标识棋盘列数
#define COL 3

/// <summary>
/// <函数声明> 游戏菜单 </函数声明>
/// </summary>
void Menu();

/// <summary>
/// <函数声明> 游戏逻辑 </函数声明>
/// </summary>
void Game();

/// <summary>
/// <函数声明> 初始化棋盘 </函数声明>
/// </summary>
void InitBoard(char board[ROW][COL], int row, int col);

/// <summary>
/// <函数声明> 打印显示棋盘 </函数声明>
/// </summary>
void DisplayBoard(char board[ROW][COL], int row, int col);

/// <summary>
/// <函数声明> 玩家下棋 </函数声明>
/// </summary>
void PlayerMove(char board[ROW][COL], int row, int col);

/// <summary>
/// <函数声明> 电脑下棋 </函数声明>
/// </summary>
void ComputerMove(char board[ROW][COL], int row, int col);

/// <summary>
/// <函数声明> 判断游戏输赢 </函数声明>
/// </summary>
/// <returns> 玩家赢="*" 电脑赢="#" 平局="q" 继续="c" </returns>
char IsWinningAndLosing(char board[ROW][COL], int row, int col);**文件> game.c**
  • 文件> game.c
#include "game.h"

/// <summary>
/// <函数实现> 游戏菜单 </函数实现>
/// </summary>
void Menu() {
	printf("***********************************************\n");
	printf("***********************************************\n");
	printf("**************  1.play     0.exit  ************\n");
	printf("***********************************************\n");
	printf("***********************************************\n");
}


/// <summary>
/// <函数实现> 初始化棋盘 </函数实现>
/// </summary>
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] = ' ';
		}
	}
}

/// <summary>
/// <函数实现> 打印显示棋盘 </函数实现>
/// </summary>
void DisplayBoard(char board[ROW][COL], int row, int col) {
	for (int i = 0; i < row; i++) {
		// 打印数据
		for (int j = 0; j < col; j++) {
			printf(" %c ", board[i][j]);
			if (j < col - 1) {
				printf("|");
			}
		}
		printf("\n");

		// 打印分割行
		if (i < row - 1) {
			for (int j = 0; j < col; j++) {
				printf("---");
				if (j < col - 1) {
					printf("|");
				}
			}
			printf("\n");
		}
	}

	printf("\n");
}

/// <summary>
/// <函数实现> 玩家下棋 </函数实现>
/// </summary>
void PlayerMove(char board[ROW][COL], int row, int col) {
	int x = 0, y = 0; 

	while (true) {
		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");
				continue;
			}
		}
		else {
			printf("输入的坐标非法,请重新输入!\n");
			continue;
		}
	}
}

/// <summary>
/// <函数实现> 电脑下棋 </函数实现>
/// </summary>
void ComputerMove(char board[ROW][COL], int row, int col) {
	printf("电脑下棋:> \n");
	while (true) {
		int x = rand() % row;
		int y = rand() % col;
		
		if (board[x][y] == ' ') {
			board[x][y] = '#';
			break;
		}
		else {
			continue;
		}
	}
}

/// <summary>
/// <函数实现> 判断游戏输赢 </函数实现>
/// </summary>
/// <returns> 玩家赢="*" 电脑赢="#" 平局="q" 继续="c" </returns>
char IsWinningAndLosing(char board[ROW][COL], int row, int col) {
	//> 判断行
	for (int 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];
		}
	}
	
	//> 判断列
	for (int 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];
	}

	//> 游戏继续
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			if (board[i][j] == ' ') {
				return 'c';
			}
		}
	}
	
	//> 游戏平局
	return 'q';
}

/// <summary>
/// <函数实现> 游戏逻辑 </函数实现>
/// </summary>
void Game() {
	char board[ROW][COL] = { 0 };
	char judgeRet = '\n';

	//> 初始化棋盘
	InitBoard(board, ROW, COL);

	//> 打印棋盘
	DisplayBoard(board, ROW, COL);

	//> 玩家和电脑对棋
	while (true) {
		//> 玩家下棋
		PlayerMove(board, ROW, COL);
		//> 判断输赢
		judgeRet = IsWinningAndLosing(board, ROW, COL);
		if (judgeRet != 'c') {
			break;
		}

		//> 打印棋盘
		DisplayBoard(board, ROW, COL);
		


		//> 电脑下棋
		ComputerMove(board, ROW, COL);
		//> 判断输赢
		judgeRet = IsWinningAndLosing(board, ROW, COL);
		if (judgeRet != 'c') {
			break;
		}
		
		//> 打印棋盘
		DisplayBoard(board, ROW, COL);
	}


	if (judgeRet == '*') {
		printf("<(^-^)>--------------> 玩家赢了!\n\n");
	}
	else if (judgeRet == '#') {
		printf("<(^-^)>--------------> 电脑赢了!\n\n");
	}
	else {
		printf("<(^-^)>--------------> 平局\n\n");
	}

	DisplayBoard(board, ROW, COL);
}

  • 文件> main.c
#include "game.h"

/// <summary>
/// <函数> 程序入口函数 </函数>
/// </summary>
/// <returns> 程序运行结果 </returns>
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;
}

5.2. 数组应用实例2:扫雷

  • 文件> game.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <time.h>

// <宏定义> 标识雷盘行数
#define ROW 9
#define ROWS 9 + 2

// <宏定义> 标识雷盘列数
#define COL 9
#define COLS 9 + 2

// <宏定义> 标识雷盘的雷数(简单)
#define EASY_COUNT 10

/// <summary>
/// <函数声明> 游戏菜单 </函数声明>
/// </summary>
void Menu();

/// <summary>
/// <函数声明> 游戏逻辑 </函数声明>
/// </summary>
void Game();

/// <summary>
/// <函数声明> 初始化雷盘 </函数声明>
/// </summary>
void InitBoard(char board[ROWS][COLS], int rows, int cols, char flag);

/// <summary>
/// <函数声明> 打印显示雷盘 </函数声明>
/// </summary>
void DisplayBoard(char board[ROWS][COLS], int row, int col);

/// <summary>
/// <函数声明> 布置雷区 </函数声明>
/// </summary>
void SetMine(char board[ROWS][COLS], int row, int col);

/// <summary>
/// <函数声明> 排查雷区 </函数声明>
/// </summary>
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
  • 文件> game.c

#include "game.h"

/// <summary>
/// <函数实现> 游戏菜单 </函数实现>
/// </summary>
void Menu() {
	printf("***********************************************\n");
	printf("***********************************************\n");
	printf("**************  1.play     0.exit  ************\n");
	printf("***********************************************\n");
	printf("***********************************************\n");
}


/// <summary>
/// <函数实现> 初始化雷盘 </函数实现>
/// </summary>
void InitBoard(char board[ROWS][COLS], int rows, int cols, char flag) {
	for (int i = 0; i < rows; i++) {
		for (int j = 0; j < cols; j++) {
			board[i][j] = flag;
		}
	}
}

/// <summary>
/// <函数实现> 打印显示雷盘 </函数实现>
/// </summary>
void DisplayBoard(char board[ROWS][COLS], int row, int col) {
	printf("---- 扫雷游戏 ----\n");
	for (int i = 0 ; i <= col; i++) {
		printf("%d ", i);
	}
	printf("\n");

	for (int i = 1; i <= row; i++) {
		printf("%d ", i);

		for (int j = 1; j <= col; j++) {
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("---- 扫雷游戏 ----\n\n");
}

/// <summary>
/// <函数实现> 布置雷区 </函数实现>
/// </summary>
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--;
		}
	}
}

/// <summary>
/// <函数声明> 获取当前坐标雷的个数 </函数声明>
/// </summary>
int get_mine_count(char board[ROWS][COLS], int x, int y)
{
	return (board[x - 1][y] +
		board[x - 1][y - 1] +
		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');
}

/// <summary>
/// <函数声明> 排查雷区 </函数声明>
/// </summary>
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++;
					//> 统计mine数组中x,y坐标周围有几个雷
					int count = get_mine_count(mine, x, y);
					show[x][y] = count + '0';//> 转换成数字字符
					DisplayBoard(show, ROW, COL);
				}
			}
		}
		else
		{
			printf("输入的坐标非法,请重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}

/// <summary>
/// <函数实现> 游戏逻辑 </函数实现>
/// </summary>
void Game() {
	//> 存放布置好雷的信息
	char mine[ROWS][COLS] = { 0 };
	InitBoard(mine, ROWS, COLS, '0');
	
	//> 存放排查出雷的信息
	char show[ROWS][COLS] = { 0 };
	InitBoard(show, ROWS, COLS, '*');
	
	//> 布置雷
	SetMine(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);
	// DisplayBoard(mine, ROW, COL);

	//> 排查雷
	FindMine(mine, show, ROW, COL);
}
  • 文件> main.c

#include "game.h"

/// <summary>
/// <函数> 程序入口函数 </函数>
/// </summary>
/// <returns> 程序运行结果 </returns>
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;
}
  • 18
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值