目录
- 数组如果初始化,可以不指定数组大小。并且数组大小不能传入一个变量。
- 数组是一组相同类型元素的组合:int类型数组只能存储int类型、char类型数组只能存储char类型
- 数组在内存方面存储的时候,数组中元素的内存地址是连续的。
- 所有的数组都是拿“首元素的内存地址”作为整个数组对象的内存地址。
数组中首元素的内存地址作为整个数组对象的内存地址。
- 数组中每一个元素都是有下标的,下标从零开始,以1递增。最后一个元素的下标:数组长度-1
下标非常重要,我们对数组中元素进行“存取”的时候,都要通过下标来进行
一维数组
-
一维数组的创建和初始化
#include <stdio.h> #include <string.h> int main() { //一维数组的定义及初始化 //数据元素类型 数组名[常量/常量表达式]={}; //C99之前,[]中必须是一个常量,不能使用变量。 //C99语法:变长数组:数组的大小可以是变量。不建议这样使用 //int n = 3; //int arr[n]; //完全初始化 //int arr[10] = {1,2,3,4,5,6,7,8,9,}; //不完全初始化,其余值为0。 //int arr[10] = {1,2,3,4,5}; //这也是不完全初始化,我们只是为下标为1的元素初始化值为10,其他默认初始化值为0。 //int arr[10] = {10}; //数组创建时,可以不指定数组大小,但是需要初始化。此时,数组根据其初始化元素个数来确定其大小。 //int arr[] ={1,2,3,4,5}; //等同于int arr[5] ={1,2,3,4,5}; //字符数组的初始化 //char ch1[5] ={'d','o','g'}; //其中存储的是'd','o','g','\0','\0' //char ch2[] ={'d','o','g'}; //其中存储的是'd','o','g' //因为字符串是以\0结尾的,所以如果是以字符串为值初始化,则这个\0也会存储到字符数组中 //char ch3[5] = "cat"; //其中存储的是'c','a','t','\0','\0' 最后面这个0是默认补充的 //char ch4[] = "cat"; //其中存储的是'c','a','t','\0' ,数组大小为4 //以下两种数组初始化的区别 char ch5[] ="dog"; char ch6[] ={'d','o','g'}; //以%s形式打印,遇到\0才会停止打印 printf("%s\n",ch5);//dog //ch5数组中存储了dog'\0',当打印时碰到\0,就会停止 printf("%s\n",ch6);//dogdog //ch6数组中只存储了dog,打印完dog不会停止,直到碰见\0才会停止打印 //求字符串长度也是,碰到\0才停止。 printf("%d\n",strlen(ch5)); //3 printf("%d\n",strlen(ch6)); //6,这里的6是个随机值。什么时候碰到\0,什么时候停止计算长度。 return 0;
-
一维数组的使用
//数组的大小需要通过计算:数组总大小/一个数组元素大小 = 数组大小。 //数组中元素下标从0开始,到(数组大小-1)结束。使用下标引用,也就是[]访问数组元素。 #include <stdio.h> int main() { //数组的不完全初始化 int arr[10] = {0}; //计算数组的元素个数 int sz = sizeof (arr)/sizeof (arr[0]); //对数组中某个元素赋值,需要通过数组下标访问。 int i; for( i=0 ; i<10 ; i++) { arr[i] = i; printf("%d ",arr[i]); //0 1 2 3 4 5 6 7 8 9 } return 0; }
-
一维数组在内存中的存储
-
一维数组在内存中是连续存放的。并且随着数组下标的增长,地址是由低到高变化的。·
//%p —— 按地址的格式打印,内存地址以十六进制存储的。 //%x —— 打印十六进制。 //%p与%x的区别:%x会省略掉前面的0,%p不会省略。 int main() { //一维数组在内存中是连续存放的。并且随着数组下标的增长,地址是由低到高变化的。 int arr[10] = {0}; int i; for(i=0 ; i<10 ; i++) { printf("&arr[%d] = %p\n",i,&arr[i]); } //这里每个数组元素的内存地址差4,是因为int类型占4个字节。 //&arr[0] = 000000d0819ff800 //&arr[1] = 000000d0819ff804 //&arr[2] = 000000d0819ff808 //&arr[3] = 000000d0819ff80c //&arr[4] = 000000d0819ff810 //&arr[5] = 000000d0819ff814 //&arr[6] = 000000d0819ff818 //&arr[7] = 000000d0819ff81c //&arr[8] = 000000d0819ff820 //&arr[9] = 000000d0819ff824 return 0; }
-
使用指针变量
#include <stdio.h> int main() { int arr[10] = {1,3,5,7,9,11,13,15,17,19}; int* p = arr; //数组名是数组首元素的地址 int i; for(i=0 ; i<10 ; i++) { //*p 解引用 printf("%d ",*p); //1 2 3 4 5 6 7 8 9 10 //因为p是指针变量,又是int类型,所以一次跳4个。如果是char类型指针,则+1指每次跳一个。 p ++; } return 0;
-
二维数组
-
二维数组的创建和初始化
//二维数组是一个特殊的一维数组,特殊在这个一维数组中的每一个元素都是一个数组。 #include <stdio.h> int main() { //二维数组的完全初始化。[0][0-3]:1、2、3、4 , [1][0-3]:5、6、7、8 [2][0-3]:9、10、11、12 //int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; //不完全初始化。没有初始化的赋值0。如果是字符数组,则赋值'\0'。 //[0][0-3]:1、2、3、4 , [1][0-3]:5、6、7、0 [2][0-3]:0、0、0、0 //int arr[3][4] ={1,2,3,4,5,6,7}; //二维数组如果在定义的同时初始化,是可以省略行的,也就是第一个[]里的值。它可以根据初始化的内容,来补全[]。 // 这里就是arr[2][4] ,因为是有六个元素,[2]够用,所以是[2][4]。如果里面有9个元素,则补全为[3][4] //[0][0-3]:1、2、3、4 , [1][0-3]:5、6、0、0 //int arr[][4] = {1,2,3,4,5,6}; //这样初始化,其元素值: //[0][0-3]:1、2、0、0 , [1][0-3]:3、4、0、0 [2][0-3]:5、6、0、0 //int arr[3][4] = {{1,2},{3,4},{5,6}}; return 0; }
-
二维数组的使用
/* 关于二维数组中元素的:读和改 a[二维数组中一维数组的下标][二维数组中的一维数组中的元素下标] a[0][0]:表示第一个一维数组中的第一个元素。 a[3][100]:表示第四个一维数组中的第101个元素。 对于a[3][100]来说,其中a[3]是一个整体,[100]是前面a[3]直接结束的结果然后再下标100, */ #include <stdio.h> int main() { int arr[3][4] ={0}; int i,j; for(i=0 ; i < 3 ; i++) { for(j=0 ; j<4 ; j++) { arr[i][j] = i*4 + j +1; printf("%d ",arr[i][j]); //1 2 3 4 //5 6 7 8 //9 10 11 12 } printf("\n"); } return 0; }
-
二维数组在内存中的存储
#include <stdio.h> int main() { int arr[3][4] ={0}; int i,j; for(i=0 ; i < 3 ; i++) { for(j=0 ; j<4 ; j++) { printf("arr[%d][%d] = %p\n",i,j,&arr[i][j]); } printf("\n"); } //arr[0][0] = 000000fb643ffc00 //arr[0][1] = 000000fb643ffc04 //arr[0][2] = 000000fb643ffc08 //arr[0][3] = 000000fb643ffc0c // //arr[1][0] = 000000fb643ffc10 //arr[1][1] = 000000fb643ffc14 //arr[1][2] = 000000fb643ffc18 //arr[1][3] = 000000fb643ffc1c // //arr[2][0] = 000000fb643ffc20 //arr[2][1] = 000000fb643ffc24 //arr[2][2] = 000000fb643ffc28 //arr[2][3] = 000000fb643ffc2c return 0; }
解引用
#include <stdio.h> int main() { int arr[3][4] = {{1,2},{3,4},{5,6}}; int i,j; int* p = &arr[0][0]; //这里的&arr[0][0]等同于arr,因为数组存储的是首元素的地址 for(i=0 ; i < 12 ; i++) { printf("%d ",*p); //1 2 0 0 3 4 0 0 5 6 0 0 p++; } return 0; }
数组越界
#include <stdio.h>
/*
* 数组的下标是有范围限制的。数组下标从0开始,如果有n个元素,最后一个元素的下标就是n-1
* 如果是使用[]方法数组下标时,小于0或>n-1了,就发生数组越界。
* C语言本身没有做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,也不意味着程序就是正确的。
* 所以我们在写程序时,最好自己检查一下引用是否越界。
*/
int main()
{
int arr[] = {1,2,3,4,5};
int i;
for(i=0 ; i<=10 ; i++)
{
//从5之后的数字都不是我们定义的数组中的数据,数组越界之后会取出的值不可预料。所以要做好检查。
printf("%d ",arr[i]); //1 2 3 4 5 32759 0 7 -641728848 78 990385215
}
return 0;
}
数组作为函数参数
函数的形参定义成数组也可以、定义成指针变量也可以。
- 数组作为函数参数传参时,实际传入的是数组名,而数组名是数组中首个元素的地址。所以说实际上传进去的是个指针
-
数组名
/* * 数组名是首元素的地址 * * 例外: * 1. sizeof(数组名) 在这个方法中,我们传入的数组名,代表整个数组。所以这个方法计算的是整个数组的大小,单位:字节。 * 2. &数组名 加了&的数组名表示整个数组,取出的是整个数组的地址。 */ #include <stdio.h> int main() { int arr[10] = {0}; //1. 在sizeof方法中,数组名代表整个数组 int sz = sizeof (arr); printf("%d\n",sz); //40 //这三个都是00000022165ffc30。 arr与&arr[0]都代表数组首元素。&arr代表整个数组 //printf("%p\n",&arr[0]); //printf("%p\n",arr); //printf("%p\n",&arr); //让他们加1,看区别 printf("%p\n",arr); //000000b6953ffcf0 printf("%p\n",arr+1); //000000b6953ffcf4 printf("%p\n",&arr); //000000b6953ffcf0 十进制是:784,188,046,576 printf("%p\n",&arr+1); //000000b6953ffd18 十进制是:784,188,046,616 //可以看出 arr+1,只加了4个字节;而&arr+1,加了40个字节。 return 0; }
-
冒泡排序
/* 冒泡排序算法 1、每一次循环结束之后,都要找出最大的数据,放到参与比较的这堆数据的最右边。(冒出最大的那个气泡) 2、核心: 拿着左边的数字和右边的数字比对,当 左边 > 右边 的时候,交换位置。 原始数据: 3,2,7,6,8 第一次循环:(最大的跑到最右边) 2,3,7,6,8 3和2比较,2<3,所以2和3交换位置 2,3,7,6,8 虽然不需要交换位置:但是3和7还是需要比较一次 2,3,6,7,8 6<7 6和7交换位置 2,3,6,7,8 虽然不需要交换位置:但是7和8还是需要比较一次 经过第一次循环,此时剩下参与比较的数据:2,3,6,7 第二次循环 2,3,6,7 2和3比较,不需要交换位置 2,3,6,7 3和6比较,不需要交换位置 2,3,6,7 6和7比较,不需要交换位置 经过第二次循环,此时剩下参与比较的数据:2,3,6 第三次循环 2,3,6 2和3比较,不需要交换位置 2,3,6 3和6比较,不需要交换位置 经过第三次循环,此时剩下参与比较的数据:2,3 第四次循环 2,3 2和3比较,不需要交换位置 原始数据:9 8 10 7 6 0 11 第一次循环: 8 9 10 7 6 0 11 第1次比较后:交换 8 9 10 7 6 0 11 第2次比较后:不交换 8 9 7 10 6 0 11 第3次比较后:交换 8 9 7 6 10 0 11 第4次比较后:交换 8 9 7 6 0 10 11 第5次比较后:交换 8 9 7 6 0 10 11 第6次比较后:不交换 最终冒出的最大数据在右边:11 经过第一次循环,此时剩下参与比较的数据: 8 9 7 6 0 10 第二次循环 8 9 7 6 0 10 第1次比较后:不交换 8 7 9 6 0 10 第2次比较后:交换 8 7 6 9 0 10 第3次比较后:交换 8 7 6 0 9 10 第4次比较后:交换 8 7 6 0 9 10 第5次比较后:不交换 最终冒出的最大数据在右边:10 经过第二次循环,此时剩下参与比较的数据: 8 7 6 0 9 第三次循环 7 8 6 0 9 第1次比较后:交换 7 6 8 0 9 第2次比较后:交换 7 6 0 8 9 第3次比较后:交换 7 6 0 8 9 第4次比较后:不交换 最后冒出的最大数据在右边:9 经过第三次循环,此时剩下参与比较的数据:7 6 0 8 第四次循环 6 7 0 8 第1次比较后:交换 6 0 7 8 第2次比较后:交换 6 0 7 8 第3次比较后:不交换 最后冒出的最大数据在右边:8 经过第四次循环,此时剩下参与比较的数据:6 0 7 第五次循环 0 6 7 第1次比较后:交换 0 6 7 第2次比较后:不交换 最后冒出的最大数据在右边:7 经过第五次循环,此时剩下参与比较的数据:0 6 第六次循环 0 6 第1次比较后:不交换 //7条数据比6次 //6条数据比5次 //5条数据比4次 //4条数据比3次 //3条数据比2次 //2条数据比1次 */ #include <stdio.h> void bubble_sort(int arr[],int length) { int i = 0; int j = 0; //10条数据要循环9次,每次经过9次判断。 n条数据,循环n-1次,判断n-1次 //数组长度是:length。要循环n-1次,所以这里是length-1 for(i=length-1 ; i > 0 ; i--) { for(j=0 ; j<i ; j++) { //内层循环里,j < i,就是几条数据循环(数据条数-1)次,如有10条数据,就循环9次 //交换位置 if(arr[j]>arr[j+1]) { int temp; temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } int main() { int arr[] = {9,8,10,6,2,11,23,18,42,16}; int sz = sizeof(arr)/sizeof(arr[0]); //冒泡排序——将较大的数字放到右边 bubble_sort(arr,sz); //数组传参的时候,传递的是首元素的地址。 int i; for (i = 0; i <sz ; ++i) { printf("%d ",arr[i]); //2 6 8 9 10 11 16 18 23 42 } return 0; }
数组的应用实例1:三子棋
在此项目的实现中,遗留问题:
- 电脑下棋太随机,有点笨,需要更改其电脑下棋实现来使他聪明一点
- 此项目中对三子棋的判断条件解了耦合,也就是说,如果是5行5列,或其他行其他列。我们的判断条件也不会有问题。(仅是行数和列数相同的时候,不相同的时候判断条件还比较多,可以自行修改实现)
-
game.h 游戏的函数声明
//关于游戏相关的函数声明,符号声明,头文件的包含。 #ifndef FIRST_GAME_H #define FIRST_GAME_H //头文件的包含 #include <stdio.h> #include <time.h> #include <stdlib.h> #define ROW 3 #define COL 3 //声明InitBoard()初始化函数 void InitBoard(char board[ROW][COL],int row,int col); //声明DisplayBoard()函数 void DisplayBoard(char board[ROW][COL],int row,int col); //声明playerMove()函数 ,第一个[]中的内容可以省略 void playerMove(char board[][COL],int row,int col); //声明computerMove() void computerMove(char board[ROW][COL],int row,int col); //声明isWin() char isWin(char board[][COL],int row,int col); #endif //FIRST_GAME_H
-
game.c 游戏函数实现
//游戏相关的函数实现 //包含头文件 #include "game.h" //InitBoard()函数的实现 void InitBoard(char board[ROW][COL],int row,int col) { int i,j; for ( i=0 ; i <row ; ++i) { for( j=0 ; j<col ; ++j) { board[i][j] = ' '; } } } /* + --- + --- + --- + //循环开始前打印 | N | D | L | + --- + --- + --- + | a | A | a | + --- + --- + --- + | a | A | a | + --- + --- + --- + //循环中打印。 */ //DisplayBoard()函数的实现 void DisplayBoard(char board[ROW][COL],int row,int col) { //打印头行 int m = row; while(m--) { printf("+ --- "); } printf("+\n"); //重置m,以供循环后尾行的输出 m = row; int i,j; for ( i=0 ; i <row ; m=row, ++i) { for( j=0 ; j<col ; ++j) { printf("| %c ",board[i][j]); //为最后一个元素打印| ,如果是最后一列,则在其后打印| if(col-j == 1) { printf("|\n"); } } //每一行打印完都打印分割行 while(m--) { printf("+ --- "); } printf("+\n"); } } //玩家下棋 void playerMove(char board[][COL],int row,int col) { //定义坐标变量 int x,y; while(1) { printf("玩家(*)请下棋:"); scanf("%d %d",&x,&y); //检查输入坐标是否合法 if( x>=1 && x<=row && y>=1 && y<=col) { //玩家通过坐标进行下棋,如1 1就对应数组里的0 0 。如果为' '空,则说明没有被占用 if(board[x-1][y-1] ==' ') { board[x-1][y-1] = '*'; //每人只能下一次棋,所以下棋后退出循环 break; } else { printf("该位置已有棋子,请重下棋子。\n"); } } else { printf("输入的坐标不合法,请重新输入。\n"); } } } //电脑下棋 void computerMove(char board[ROW][COL],int row,int col) { printf("电脑(#)下棋:\n"); //可能取的这个随机坐标,已经有值了,所以我们把它放入循环 while(1) { //电脑随机走 ,其x与y都需要0~2的随机值。我们使用rand()%3就可以得到这个范围。这里使用row与col,解耦合。 int x = rand()%row; int y = rand()%col; if(board[x][y] == ' ') { board[x][y] = '#'; //只有当下棋了之后,结束循环 break; } } } //判断局势 // 返回*表示玩家获胜、返回#表示电脑获胜、返回C表示游戏继续、返回Q表示平局 char isWin(char board[][COL],int row,int col) { int i,j; int flag = 1; //每一行或每一列、每个对角线判断后都需要将flag重置为1 //如果有一个为空,就说明还不到获胜的点,所以如果为空,也返回flag=0 //判断三行 for(i=0 ; i<row ; flag=1,i++) { for(j=0 ; j<col-1 ;j++) { //与下一个比较,只要一次是不相等的,就不能算是获胜,如果不相等,则flag=0。 if(board[i][j] == ' ' || (board[i][j] != board[i][j+1])) { flag = 0; } else{ } } if(flag == 1) { //如果flag=1说明flag=0没有执行,这一行都是相同的字符。 return board[i][0]; } } //判断三列 for(i=0 ; i<col ; flag=1,i++) { //列的比较:[0][0] [1][0] [2][0] for(j=0 ; j<row-1 ;j++) { //与下一个比较,只要一次是不相等的,就不能算是获胜,如果不相等,则flag=0。 if( board[j][i] == ' ' || (board[j][i] != board[j+1][i])) { flag = 0; } } if(flag == 1) { //如果flag=1说明flag=0没有执行,这一列都是相同的字符。 return board[0][i]; } } //判断左对角线是否相等 。[0][0] [1][1] [2][2] 判断两次 for(i=0 ; i<row-1 ; i++) { if(board[i][i] == ' ' || (board[i][i] != board[i+1][i+1])) { flag = 0; } } if(flag == 1) { //因为这里出了循环,但是在对角线上,所以取其左上角坐标即可 return board[0][0]; } //左线判断完,重置为1 flag = 1; //判断右对角线是否相等。 比较:[2][0] [1][1] [0][2],比较列数-1次。 //因为右对角线只有一条,所以放在一起判断,要一起变。 for(i=row-1,j=0 ;i>0 ;j++,i--) { if( board[i][j] == ' ' || (board[i][j] != board[i-1][j+1])) { flag = 0; } } if(flag == 1){ //这里出了循环,而且是右对角线,取右上角 return board[col-1][0]; } //到这里,说明没有出现胜利的。判断棋盘是否为空 for(i=0 ; i<row ; i++) { for(j=0 ; j<col ; j++) { //判断是否还有位置是空 if(board[i][j] == ' ') { //如果还有位置为空,返回C,游戏继续 return 'C'; } } } //此时棋盘不为空,并且没有获胜者出现,说明平局 return 'Q'; }
-
test.c 游戏测试
//测试游戏逻辑 #include "game.h" void menu() { printf("**------ GAME:三子棋 -------**\n"); printf("**----- 输入1开始游戏 -------**\n"); printf("**------- 输入0退出 --------**\n"); printf("**-------------------------**\n"); } void game() { //二维数组存储棋子。 ROW与COL是常量,定义在game.h头文件中 char board[ROW][COL]; //初始化棋盘,所有棋子为空格。 InitBoard(board,ROW,COL); //打印棋盘:打印二维数组 DisplayBoard(board,ROW,COL); char ret = 'C'; if(rand()%5 == 0) { //如果这里是1,则电脑先下棋。 //电脑下棋 printf("电脑:这次我先走两步,给你一点难度!"); computerMove(board,ROW,COL); computerMove(board,ROW,COL); //下棋后打印 DisplayBoard(board,ROW,COL); } while (ret == 'C') { //玩家下棋 playerMove(board,ROW,COL); //下棋后打印 DisplayBoard(board,ROW,COL); //下棋后判断,是否玩家获胜 ret = isWin(board,ROW,COL); if(ret == '*' || ret == 'Q') { break; } //电脑下棋 computerMove(board,ROW,COL); //下棋后打印 DisplayBoard(board,ROW,COL); //下棋后判断,是否电脑获胜 ret = isWin(board,ROW,COL); if(ret == '#' || ret == 'Q') { break; } } if(ret == '*') { printf("YOU ARE WIN !!\n"); } else if(ret == '#') { printf("电脑 WIN !!\n"); } else { printf("~平局~1\n"); } } int main() { int input = 0; //使用时间戳初始化种子,*5增大时间间隔 srand((unsigned int)time(NULL) *5); do { menu(); printf("请输入:"); scanf("%d",&input); switch (input) { case 1: printf("游戏开始。以行数、列数来指定下棋坐标,如1 1表示第一行第一列\n"); game(); break; case 0: printf("已退出游戏"); break; default: printf("输入有误,请重新输入!"); } }while(input); return 0; }
数组的应用实例2:扫雷游戏
在此项目的实现中,遗留问题:
- 在其他扫雷游戏中,如果点击了一个点,这个点周围只要没有雷,会把这个坐标周围8个点没有雷的都展现出来。并且将这个点周围的八个点周围的雷数量扫出来,并显示在该点位置。并继续判断这8个点周围有没有雷(在这里注意,在判断八个点周围是不是雷的的时候,已经判断过的会被重复判断。我们要进行限制,判断过的不再判断),直到所有未知区域旁边已经挖开的点都存储的是雷的数量的时候,才停止。
这就是在扫雷游戏中,如果点了一个点,就会展开一大片的原因。
直到这一圈周围都有雷的时候就不会再展开
思路:使用递归。
- 并且在其他扫雷中,如果你觉得这个地方是雷,可以右击鼠标进行标记。
当选择坐标之后,提供选项,是挖雷还是将其标记为雷
-
game.h 游戏函数的声明
#ifndef FIRST_GAME_H #define FIRST_GAME_H #include <stdio.h> #include <stdlib.h> #include <time.h> //定义雷的数量 #define NUM_MINE 10 //虽然我们的数组大小是11*11的,但是我们在布置雷的时候,还是一个9*9的范围,这里这样定义更方便使用。 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 //声明初始化数组的函数声明 void initBoard(char board[ROWS][COLS],int rows, int cols,char set); //声明打印数组的函数声明 void printBoard(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); #endif //FIRST_GAME_H
-
game.c 游戏函数的实现
#include "game.h" //初始化数组 函数的实现 void initBoard(char board[ROWS][COLS],int rows, int cols,char set) { int i,j; for(i=0 ; i<rows ; i++) { for(j=0 ; j<cols ; j++) { board[i][j] = set; } } } //打印数组的函数实现 void printBoard(char board[ROWS][COLS],int row,int col) { int i,j; printf("—————————————— = = = = = !!雷来了!! = = = = = = = ———————————\n "); //打印列数 for(i=1 ; i<= col ; i++) { if(i == 1) { printf("|"); } printf(" %d |",i); } printf("\n --- "); //打印头行的+-。有几列,就循环几次。 int m =col; while(m--) { printf("+ --- "); } printf("+\n"); //重置m,以供打印尾行 m = col; //因为是打印中间的九行,所以横竖坐标都是从1~9 for(i=1 ; i<=row ; m=col,i++) { //打印行号 printf(" %d ",i); for(j=1 ; j<=col ; j++) { printf("| %c ",board[i][j]); //如果j是最后一列,则还要打印一个| if(j == col) { printf("|\n"); } } printf(" --- "); while(m--) { printf("+ --- "); } printf("+\n"); } printf("\n"); } //setMine函数实现 void setMine(char board[ROWS][COLS],int row,int col) { //布置10个雷 int count = NUM_MINE; while (count) { //产生随机坐标。向9取余,余数范围0~8,加一个1范围就成了1~9 int x = rand()%row+1; int y = rand()%col+1; if(board[x][y] == '0') { board[x][y] = '1'; count--; } } } //找坐标周围有几个雷的函数 //因为我们要返回雷的个数,应该是一个int类型的数据。而我们的show数组是char类型的。 //在ASCII编码表中,'0'对应48,'1'对应49,'2'对应50,'3'对应51.... //所以我们只要将取来的字符减去一个'0'就可以得到int。 //如'1'对应49,'0'对应48,'1'-'0'=1。 //这个函数是支撑扫雷函数执行的,所以只在内部用。如果部想被外部引用,则可以加上static static int mineCount(char mine[ROWS][COLS],int x,int y) { /* * 一圈的坐标 * mine[x-1][y-1] mine[x-1][y] mine[x-1][y+1] * mine[x][y-1] mine[x][y] mine[x][y+1] * mine[x+1][y-1] mine[x+1][y] mine[x+1][y+1] */ //返回这除了mine[x][y]之外的八个坐标所对应的字符和,减去8个'0',就是雷的个数 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' ; //使用循环的方式 // int count = 0; // int i,j; // for(i=-1 ; i<=1 ; i++) // { // for(j=-1 ; i<=1 ; i++) // { // //当x=0,y=0的时候跳出本次循环 // if(x==0 && y==0) // { // continue; // } // //如果当前坐标是一个雷,则count+1。 // if(mine[x+i][y+i] == '1') // { // count++; // } // } // } // return count; } //findMine()函数实现 void findMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col) { int x,y; //排查雷是一个很漫长的过程,要排查很多次,所以这里应该放入循环 int win = 0; //循环次数: while(win <row*col - NUM_MINE) //行数*列数-雷数。 { //输入坐标,检查坐标是不是雷。是雷——被炸死了游戏结束 ; 不是雷,存储排查的信息到show数组,游戏继续。 printf("请输入要挖雷的坐标:"); scanf("%d %d",&x,&y); //判断坐标的合法性 if(x>=1 && x<=row && y>=1 && y<=col) { //如果坐标合法,就进行判断 if(mine[x][y] == '1') { printf("很遗憾,你挖到了雷,被炸死了呢(^_^) \n"); //打印雷数组 printBoard(mine,row,col); //结束循环 break; } else { //判断是不是已经挖开了 if(show[x][y] != '#') { printf("这里已经挖开了,重新挖!\n"); //如果已经挖开了就跳出本次循环。 continue; } //如果没有挖开,才进行判断 //如果不是雷,则统计周围有几个雷。因为雷是在mine数组中,传进入,以x,y为坐标点查找周围的雷 int count = mineCount(mine,x,y); //放入show数组中。 //因为count是一个int类型变量,而这里我们要存储为char类型。 //在ASCII编码表中,'0'对应48,'1'对应49,'2'对应50,'3'对应51.... //所以我们只要将取来的count加上一个'0'就可以得到char类型数据 //如'3'对应51,'0'对应48。 3+'0' = 58,就是'3' show[x][y] = count+'0'; //存入数组后,打印排查数组 printBoard(show,row,col); //如果我们排出一个位置,则win+1。直到win =row*col - NUM_MINE时,就排完雷了 win++; } } else { printf("输入的坐标不合法,请重新输入。\n"); } } //循环结束之后,判断win是不是row*col - NUM_MINE,如果是,则表示排雷成功,然后打印雷区。 if(win == row*col - NUM_MINE) { printf("雷排完啦 Nice,You are Thor!!\n"); //打印雷区 printBoard(mine,row,col); } }
-
test.c 扫雷游戏的测试
/* * 扫雷逻辑:点一个位置,以这个点为中心,周围的八个块中有几个雷,这个位置就显示数字几。 * 第一步:布置雷 * - 创建一个9*9的二维数组,来存储布置的雷。比如随机创建了10个雷 * - 是雷的地方,存储字符'1';不是雷的地方,存储字符'0' * * 第二步:排查雷 * - 如果排查出了雷,那我们要放数字1。此时与存放雷的'1'就产生了冲突。 * 此时,可以将存放的雷存储为*,不是雷的存储为#,排查出的雷存储为1。但是在打印的时候要一个一个判断,太麻烦了。 * - 我们可以在创建一个数组,其中专门存放排查出的信息,打印时只打印这个数组。 * 比如点了一个位置,这个位置周围有两个雷,这个位置就存放2。 * * 所以就是创建了两个数组: * - 一个数组存储布置的雷; 一个数组存储排除出雷的信息。 * - 类型都是char,这样打印的时候以%c打印就可以了。 * * - 我们如果要扫四个角,那么可能会出现数组越界。 * - 所以我们定义存放排查信息的数组时,就可以定义大一点,9*9上下左右加一行。第二个数组就需要定义为11*11的二维数组 * - 为了更方便编写,我们可以将存放雷的数组也定义为11*11,这样就与另一个数组可以一一对应了。 * 而实际上我们布置雷的时候,还是在中间布置,不会不知道外围的一圈。 */ #include "game.h" void menu() { printf("**------ GAME:扫 雷 -------**\n"); printf("**----- 输入1开始游戏 -------**\n"); printf("**------- 输入0退出 --------**\n"); printf("**-------------------------**\n"); } void game() { //定义数组,解耦合:将常量定义到头文件中。 char mine[ROWS][COLS] = {0}; //存放布置的雷 char show[ROWS][COLS] = {0}; //存放排查出雷的信息 //初始化数组函数 initBoard(mine,ROWS,COLS,'0'); initBoard(show,ROWS,COLS,'#'); //打印数组的函数。打印的时候我们只打印其中有雷的区域,没有的我们不打印。 //打印show数组,让玩家来挖雷。 printBoard(show,ROW,COL); //布置雷:只在下标为1~9的区域布置雷 setMine(mine,ROW,COL); //给我们自己看的雷区,用于验证。 //printBoard(mine,ROW,COL); //排查雷:传入两个数组,因为是在1~9的范围排雷,所以传ROW与COL findMine(mine,show,ROW,COL); } int main() { int input = 0; //使用时间戳初始化种子,*5增大时间间隔 srand((unsigned int)time(NULL) *5); do { menu(); printf("请输入:"); scanf("%d",&input); switch (input) { case 1: printf("扫雷吧,少年!!\n"); game(); break; case 0: printf("已退出游戏"); break; default: printf("输入有误,请重新输入!\n"); } }while(input); return 0; }
数组练习
-
逆序数组元素并打印
#include <stdio.h> // void reverse(int arr[],int sz) { //起始下标 int begin = 0; //结束下标 int end = sz-1; //第一个和最后一个交换,然后第二个跟倒数第二个交换 while(begin<end) { int temp = arr[begin]; arr[begin] = arr[end]; arr[end] = temp; //左边的往右走,右边的往左走 begin++; end--; } } int main() { int arr[10] ={11,99,88,75,24,12,53,66,42,38}; int sz = sizeof(arr)/sizeof(arr[0]); reverse(arr,sz); int i; for(i = 0; i<sz ; i++) { printf("%d ",arr[i]);//38 42 66 53 12 24 75 88 99 11 } return 0; }
-
两个数组一样大,交换其中的元素。
//交换两个数组中的元素 int main() { int arr1[] ={1,3,5,7,9}; int arr2[] ={2,4,6,8,10}; int sz = sizeof(arr1)/sizeof(arr1[0]); int i; for(i=0 ; i<sz ; i++) { int temp = arr1[i]; arr1[i] = arr2[i]; arr2[i] = temp; } return 0; }
-
编程实现,输入年份、月份,就可以输出这一年的这一个月有多少天
//编程实现,输入年份、月份,就可以输出这一年的这一个月有多少天 //闰年的2月是29天,其他年的2月是28天 // #include <stdio.h> int main() { int year,month; //我们在最前面加一个0,这样是几月,下标为几,就对应这个月的天数。 int days[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; //如果读取正确,则进入循环 printf("输入年份和月份:"); while (scanf("%d %d",&year,&month) != EOF) { int day = days[month]; if(year%4==0&&year%100!=0 || year%400 == 0) { if(month ==2) { day++; } } printf("%d\n",day); } return 0; }
-
在一个有序序列中插入数据,并且要求插入后还是有序的
/* * 有一个有序数字序列,从小到大排列,将一个新输入的数据擦换如到序列中,保证插入新数后,序列仍然是升序。 * 输入: * 第一行输入一个整数,0≤n≤50 ,也就是说最大是50个数。 * 第二行输入n个升序牌系列的整数,输入用空格分割的n个整数。 * 第三行输入要进行插入的一个整数。 * * 输出,插入了数据后的有序排列的整数 */ #include <stdio.h> int main() { //序列最多是50个数,算上要插入的数据,应该是最多51个数,所以下标定义为50 int arr[50] = {0}; //整数序列 int n; printf("多少个数的序列(0≤n≤50):"); scanf("%d",&n); int i; printf("输入%d个数:",n); for(i=0 ; i<n ; i++) { scanf("%d",&arr[i]); } //要插入的数 int m; printf("要插入的数:"); scanf("%d",&m); //插入 //输入的是一个几个数的序列,就从最后一个数开始与插入的数比较。 for(i=n-1 ; i>=0 ; i--) { //如果当前元素比要插入的数大,则将当前元素放到后面的那个位置。 //此时arr[i]所在位置就可以放别的元素了。每次循环的时候,这个位置都是为了放其他数字的。 if(arr[i]>m) { arr[i+1] = arr[i]; arr[i] = m; } else { //如果当前元素≤要插入的数,则将要插入的数放在这个数后面 //arr[i+1] = m;//如果放在循环内,则话要考虑当插入的数被所有的的数都要小的这种情况。 //放完之后,已经把数据插入了有序数列,就退出循环 //break; //可以先跳出去,然后将要插入的数放在这个数后面。 //这样就算要插入的数要比所有的数小,也可以插进去。 break; } } //如果当前元素≤要插入的数,则将要插入的数放在这个数后面 //如果要插入的数比所有的数都要小。也就是说i--之后i=-1了,跳出了循环 //则说明要插入的数比所有的数都小。此时arr[-1+1] = arr[0] ,arr[0]=m,就插进去了。 arr[i+1] = m; //打印 printf("插入后:\n"); for(i=0 ; i<n+1 ; i++) { printf("%d ",arr[i]); } return 0; }