一维数组创建和初始化
数组创建数组初始化
sizeof与strlen 一维数组的使用一维数组在内存中的存储 二维数组
二维数组创建二维数组初始化二维数组使用与存储 数组越界数组作为函数参数
冒泡排序数组名是什么? 游戏
三子棋
test.c 游戏的测试逻辑game.c 游戏的实现逻辑game.h 游戏实现函数的声明细节 扫雷
test.cgame.hgame.c 练习
1.交换2数组2.数组操作3.字符串逆序4.计算每位数之和5.求n^k
一维数组创建和初始化 数组创建数组:一组相同类型的集合
type_t arr_name [const_n];
C99标准之前是不支持使用变量的,只能是常量
C99中增加了变长数组的概念,允许数组的大小是变量
要求编译器支持C99标准,但常见的编译器(VS)对C99支持不够好
Linux - Centos 7 gcc编译器
char arr3[10]; float arr4[1]; double arr5[20];数组初始化
int arr1[10] = {1,2,3};
//不完全初始化,剩余元素默认为0
int arr2[] = {1,2,3,4};
int arr3[5] = {1,2,3,4,5};
char arr4[3] = {'a',98, 'c'};
//98 是 'b'的ASCII值
char arr5[] = {'a','b','c'};//数组大小3个字节
char arr6[] = "abc";//还有一个
//数组大小4个字节
sizeof与strlen
char arr1[] = {‘a’,‘b’,‘c’};
char arr2[] = “abc”;
arr2中放的是abc’ ’
char arr1[] = { 'a', 'b', 'c' };
//arr1有3个元素,数组的大小是3个字节
printf("%dn", sizeof(arr1));
printf("%dn", strlen(arr1));//随机值
char arr2[] = "abc";//a b c
//arr2有4个元素,数组的大小是4个字节
printf("%dn", sizeof(arr2));
printf("%dn", strlen(arr2));//3
sizeof是操作符,计算变量所占空间大小,任何类型都相同
不在乎内存中是否存在
strlen是函数,只针对字符串,计算字符串长度,遇到 停止
不包括
数组如果不初始化,那么就会被赋为随机值,不可控
char acX[] = “abcdefg”;
char acY[] = {‘a’,’b’,’c’,’d’,’e’,’f’,’g’};
数组acX长度大于数组acY长度
但如果问字符串acX长度与字符串acY长度比较 则不能确定
因为acY后面没有 strlen遇到 才会停止故求出来的是随机值
[] 下标引用操作符
#includeint 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; }
//计算数组元素个数: int arr[10]; int sz = sizeof(arr)/sizeof(arr[0]); //40 / 4 = 10一维数组在内存中的存储
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
//打印数组每个元素的地址
int i = 0;
for (i = 0; i < 10; i++)
{
printf("&arr[%d] = %pn", i, &arr[i]);
printf("%pn", p + i);//p+i 是数组中arr[i]的地址
printf("%p ----- %pn", p + i, &arr[i]);
printf("%d ", * (p + i));//1 2 3 4 5 6 7 8 9 10
}
//整型指针,+1跳过一个整型元素
return 0;
}
0x0c表示十进制的12
p+i 与&arr[i] 等价
int arr[3][4]; //三行四列,每个元素是int型 char arr[3][5]; double arr[2][4];
其实就是数学里的矩阵
二维数组初始化int arr[3][4] = { {1,2},{3,4},{5,6} };
//不完全初始化
//1 2 0 0
//3 4 0 0
//5 6 0 0
//二维数组初始化不能省略列,行可以省略
#include二维数组使用与存储int main() { int arr[3][4] = { {1,2},{2,2},{3,3,3} }; int arr[][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; return 0; }
#includeint main() { int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; int i = 0; int j = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("&arr[%d][%d] = %pn", i, j, &arr[i][j]); } } for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("arr[%d][%d] = %dn", i, j, arr[i][j]); } } return 0; }
二维数组理解为一维数组的数组
在内存中是连续储存的,第二行紧跟着第一行
每一行是一个一维数组
以为是这样存储,但实际是连续存储
数组的下标如果小于0,或者大于n-1,就是数组越界访了,超出了数组合法空间的访问。
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,
所以程序员写代码时,最好自己做越界的检查。
数组前面后面还是有空间的
#includeint main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int i = 0; for(i=0; i<=10; i++) { printf("%dn", arr[i]);//当i等于10的时候,越界访问了 } return 0; }
1
2
3
4
5
6
7
8
9
10
-858993460
数组越界,程序已经错了,但并没有报错,编译器能力有限,有时候识别不出来越界
c语言支持多维数组,但是比较少用
数组作为函数参数 冒泡排序核心思想:
两两相邻元素进行比较,如果有肯能需要交换
一趟冒泡排序搞定一个数字
让当前待排序数组中的一个元素来到最终应该出现的位置上
10个元素,9躺冒泡排序
#include数组名是什么?//void bubble_sort(int* arr,int sz) 这样写也可以 //指针大小是4,不能在函数内算数组元素个数 void bubble_sort(int arr[], int sz) { int i = 0; for (i = 0; i < sz - 1; i++) { //一趟冒泡排序的过程 int j = 0; //一趟冒泡排序比较的对数 //第二趟比较8对,第三躺比较7对 for (j = 0; j < sz - 1 - i; 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,5,2,4,9,0,7,6,8 }; //设计一个函数对arr数组进行排序-冒泡排序 int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, sz);//数组名代表的就是数组 //数组传参,实际上传过去的不是整个数组,传过去的是数组首元素地址 //避免空间浪费,只需要传首元素地址,就能依据地址找到所有元素 int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; }
补充:
- sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数
组。&数组名,取出的是整个数组的地址。&数组名,数组名表示整个数组。
#includeint main() { int arr[] = { 1,2,3,4,5 }; printf("%pn", arr);//00AFF92C printf("%pn", &arr[0]);//00AFF92C printf("%pn", &arr);//00AFF92C printf("%dn", sizeof(arr));//20 printf("%pn", arr+1);//00AFF930 printf("%pn", &arr[0]+1); //00AFF930 //跳过4字节 printf("%pn", &arr+1); //00AFF940 //跳过一整个数组,20字节 //sizeof是例外 return 0; }
整个数组地址与数组首元素地址相同
数组名 == 数组首元素地址
数组地址从起始位置开始
数组地址和数组首元素地址意义不一样,值是一样的
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void menu()
{
printf("******************************n");
printf("********* 1.play *********n");
printf("********* 0.exit ********n");
printf("******************************n");
}
void game()
{
//数据存储到一个字符的二维数组中,玩家下棋'*' 电脑下棋'#'
char board[ROW][COL] = { 0 };//数组的内容应该是全部空格
InitBoard(board,ROW,COL);//初始化棋盘
DisplayBoard(board,ROW,COL);//打印棋盘
//下棋
char ret = 0;
while (1)
{
player_move(board,ROW,COL);
DisplayBoard(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')//C表示游戏继续
{
break;
}
computer_move(board, ROW, COL);
DisplayBoard(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("玩家赢n");
}
else if (ret == '#')
{
printf("电脑赢n");
}
else
{
printf("平局n");
}
}
void test()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();//菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏n");
break;
default:
printf("选择错误n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
game.c 游戏的实现逻辑
#define _CRT_SECURE_NO_WARNINGS 1
#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] = ' ';
}
}
}
//版本一 只能打印3x3棋盘
//版本二 配合宏定义可以灵活修改
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
//数据 把 %c |看成一组数据,但最后一个|不打印
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 player_move(char board[ROW][COL], int row, int col)
{
printf("玩家下棋:>");
int x = 0;
int y = 0;
while (1)
{
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 computer_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:>n");
while (1)
{
x = rand() % ROW;//0 - 2
y = rand() % COL;//0 - 2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
int is_full(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;//满了
}
//版本1 is_win被写死了,只能判断ROW COL为3的情况
//版本2 is_win灵活变动
char is_win(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
int a = 0;
//判断行
for (i = 0; i < row; i++)
{
a = 0;
for (j = 0; j < col - 1; j++)
{
if (board[i][j] == board[i][j + 1] && board[i][j] == '*')
{
a++;
}
if (board[i][j] == board[i][j + 1] && board[i][j] == '#')
{
a--;
}
if (a == col - 1)
{
return '*';
}
if (a == -col + 1)
{
return '#';
}
}
}
//判断列
for (i = 0; i < col; i++)
{
a = 0;//a需要清零
for (j = 0; j < row - 1; j++)
{
if (board[j][i] == board[j + 1][i] && board[j][i] == '*')
{
a++;
}
if (board[j][i] == board[j + 1][i] && board[j][i] == '#')
{
a--;
}
if (a == row - 1)
{
return '*';
}
if (a == -row + 1)
{
return '#';
}
}
}
//判断左对角线
for (i = 0, j = 0; i < row - 1; i++, j++)
{
if (board[i][j] == board[i + 1][j + 1] && board[i][j] == '*')
{
a++;
}
if (board[i][j] == board[i + 1][j + 1] && board[i][j] == '#')
{
a--;
}
if (a == row - 1)
{
return '*';
}
if (a == -row + 1)
{
return '#';
}
}
//判断右对角线
for (i = 0, j = col - 1; i < row - 1; i++, j--)
{
if (board[i][j] == board[i + 1][j - 1] && board[i][j] == '*')
{
a++;
}
if (board[i][j] == board[i + 1][j - 1] && board[i][j] == '#')
{
a--;
}
if (a == row - 1)
{
return '*';
}
if (a == -row + 1)
{
return '#';
}
}
//判断平局
if (1 == is_full(board, row, col))
{
return 'Q';
}
//游戏继续
return 'C';
}
game.h 游戏实现函数的声明
#pragma once #define ROW 5 #define COL 5 #include细节#include #include //初始化棋盘 void InitBoard(char board[ROW][COL],int row,int col); //打印棋盘 void DisplayBoard(char board[ROW][COL], int row, int col); //玩家下棋 void player_move(char board[ROW][COL], int row, int col); //电脑下棋 void computer_move(char board[ROW][COL], int row, int col); //判断输赢 //玩家赢了 -'*' //电脑赢了 -'#' //平局 - 'Q' //游戏继续 - 'C' char is_win(char board[ROW][COL], int row, int col);
- 棋盘数据打印是把 %c |看成一组数据但最后一个|不打印
分隔行 把—|看成一组数据但最后一个|不打印且最后一行分隔行也无需打印把要用到的头文件及函数都在头文件中声明,这样在.c文件中就只需引用自己创建的头文件,避免了头文件的重复定义用时间戳生成随机数的时候,先使用srand函数设置种子,注意srand函数不要放在实现函数的内部,不然会导致随机数生成的很近,srand函数每次游戏只需重新设置一次
9x9 二维数组存储
char board / int board 似乎都行?
先布置累
排查雷
如果用0/1表示有雷无雷 可能会产生冲突
用* #也可以 但符号太多比较麻烦
再弄一个棋盘,放排查出的雷的信息
没排查过的位置放*
所以选char board
防范数组越界,把数组再多开辟一圈就行 11x11数组 用的只是9x9
数字3+‘0’ -> ‘3’
注意不要形成死递归
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 };//存放排查出的雷的信息
//初始化mine数组-全字符'0'
InitBoard(mine,ROWS,COLS,'0');
//初始化show数组-全字符'*'
InitBoard(show,ROWS,COLS,'*');
//打印棋盘,只需打印9x9就行了
//DisplayBoard(mine, ROW, COL);
//DisplayBoard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
//DisplayBoard(mine, ROW, COL);
//排雷
FindMine(mine, show, ROW, COL);
}
void test()
{
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");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
game.h
#pragma once //头文件包含:符号的声明.函数的声明 #includegame.c#include #include #define ROW 9 #define COL 9 #define ROWS ROW + 2 #define COLS COL + 2 #define EASY_COUNT 5 //初始化棋盘 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 mine[ROWS][COLS], int row, int col); //排雷 void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
#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 DisplayBoard(char board[ROWS][COLS], int row, int col)
//虽然只打印9*9,但传过去的数组依旧是11*11
{
//1-9开始
int i = 0;
//打印列号
for (i = 0; i <= col; i++) //i从0开始,避免列号错位(多多尝试就好)
{
printf("%d ", i);
}
printf("n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印行号
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("n");
}
}
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;//布置10个雷
while (count)
{
//随机产生的x y坐标是1-9
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
//无需写到头文件,因为不想暴露它 +static保护更加彻底 只能在game.c用
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
//mine里放的是字符 需要减去'0'
return mine[x - 1][y] + mine[x - 1][y - 1]
+ mine[x][y - 1] + mine[x + 1][y - 1]
+ mine[x + 1][y] + mine[x + 1][y + 1]
+ mine[x][y + 1] + mine[x - 1][y + 1]
- 8 * '0';
//因为设计时11*11 所以x+1 y+1不会越界
}
//递归展开一片雷
void Open_Mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
if (get_mine_count(mine, x, y) == 0)
{
show[x][y] = ' ';
//判断坐标是否越界以及雷是否已经被排除
if (show[x - 1][y - 1] == '*')
Open_Mine(mine, show, x - 1, y - 1);
if (show[x - 1][y] == '*')
Open_Mine(mine, show, x - 1, y);
if (show[x - 1][y + 1] == '*')
Open_Mine(mine, show, x - 1, y + 1);
if (show[x][y - 1] == '*')
Open_Mine(mine, show, x, y - 1);
if (show[x][y + 1] == '*')
Open_Mine(mine, show, x, y + 1);
if (show[x + 1][y - 1] == '*')
Open_Mine(mine, show, x + 1, y - 1);
if (show[x + 1][y] == '*')
Open_Mine(mine, show, x + 1, y);
if (show[x + 1][y + 1] == '*')
Open_Mine(mine, show, x + 1, y + 1);
}
else
{
show[x][y] = get_mine_count(mine, x, y) + '0';
}
}
//遍历show,以便判断是否排雷完毕
int Travel(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int win = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (show[i][j] == '*')
{
win++;
}
}
}
return win;
}
//排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
//在mine数组中排雷,信息放到show数组中去
{
int x = 0;
int y = 0;
int win = 0;
while (1)
{
printf("请输入要排查的雷的坐标:>n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//坐标合法性排查
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了n");
DisplayBoard(mine, row, col);
break;
}
else
{
//计算x y坐标周围有几个雷
//show 里面放的是字符 数字3 + '0' 就变成'3'
//字符'0' 48 '2' -> 50 = '0' + 2
Open_Mine(mine, show, x, y);
DisplayBoard(show, row, col);
}
}
else
{
printf("输入坐标非法,请重新输入n");
}
win = Travel(show, row, col);
if (win == EASY_COUNT)
break;
}
//EASY_COUNT 设置小一点方便检验是否排雷成功
if (win == EASY_COUNT)
{
printf("恭喜你,排雷成功n");
DisplayBoard(mine, row, col);
}
}
扩展:
#include2.数组操作int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[] = { 10,9,8,7,6,5,4,3,2,1 }; int sz = sizeof(arr1) / sizeof(arr1[0]); for (int i = 0; i < sz; i++) { int tmp = arr1[i]; arr1[i] = arr2[i]; arr2[i] = tmp; } return 0; }
创建一个整形数组,完成对数组的操作
实现函数init() 初始化数组为全0
实现print() 打印数组的每个元素
实现reverse() 函数完成数组元素的逆置
#include3.字符串逆序void init(int arr[10], int sz) { int i = 0; for (i = 0; i < sz; i++) { arr[i] = 0; } } void print(int arr[10], int sz) { int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("n"); } void reverse(int arr[10], int sz) { int left = 0; int right = sz - 1; while (left < right) { int tmp = arr[left]; arr[left] = arr[right]; arr[right] = tmp; left++; right--; } } int main() { int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); print(arr, sz); reverse(arr, sz); print(arr, sz); init(arr, sz); print(arr, sz); return 0; }
编写一个函数 reverse_string(char* string)(递归实现)
实现:将参数字符串中的字符反向排列,不是逆序打印。
要求:不能使用C函数库中的字符串操作函数。
比如 :
char arr[] = “abcdef”;
逆序之后数组的内容变成:fedcba
递归实现:
#includeint my_strlen(char* s) { int count = 0; while (*s != ' ') { count++; s++; } return count; } //[a b c d e f g ] //递归版本 void reverse_string(char* arr) { int len = my_strlen(arr); char tmp = *arr;//a *arr = *(arr + len - 1);//找到了g *(arr + len - 1) = ' ';//先把g的内容赋值为 才能识别为字符串 if(my_strlen(arr+1)>1)//递归必须要有结束条件 reverse_string(arr+1);//arr+1找到b的地址 *(arr + len - 1) = tmp;//再把a放回到g的位置 } //参数用指针的形式 void reverse_string(char* str) { char* left = str;//a的地址 char* right = str + my_strlen(str) - 1; while (left 4.计算每位数之和 //写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和 //例如,调用DigitSum(1729),则应该返回1 + 7 + 2 + 9,它的和是19 //输入:1729,输出:19 int DigitSum(size_t n) { if (n <= 9) return n; else return DigitSum(n / 10) + n % 10; } int main() { size_t num = 0; scanf("%u", &num); int ret = DigitSum(num); printf("%dn", ret); return 0; }5.求n^kdouble Pow(int n, int k) { if (k == 0) return 1; else if (k > 0) return n * Pow(n, k - 1); else return 1.0 / Pow(n, -k); } int main() { int n = 0; int k = 0; scanf("%d %d", &n, &k); double ret = Pow(n, k); printf("%lfn", ret); return 0; }要注意考虑k<0的情况



