0.前言
1.上一篇文章中,我们重点讲述了:如何main函数的各个模块转化为函数,使得main函数简介合理,利于阅读、整理。
2.今天,则主要讲解c语言中数组的知识及其应用(包括进制转换以及两个有趣的游戏)
1.文章目的:
- 阐述数组的语法、特点等基础知识
- 通过例子来探究c语言中数组的使用方法、使用思路、注意事项
2.本文主要提及的技术
2.1数组
2.1.1数组基本概念
数组能够开辟多个连续的空间,每个存储空间都能存放一个类型相同的元素
注意事项:
(1)此概念仅针对c语言,在java等语言中,同一个数组可以存放不同类型的元素
(2)数组分配的内存空间是连续的,所以如果数组分配空间较大,可能会失败
(3)静态数组定义时必须明确知道数组的大小
2.1.2数组的优缺点
(1)数组的语法定义较为简单
(2)由于有索引值,因此“查找”操作较为轻松
(3)删除数组中央的某个元素较为困难
2.1.3数组的定义
数组的定义方式有三种:
(1)初始化里面确定数组大小
int score[] = {29,13,23,11};
(2)在定义时,明确指定数组大小
int temp[5]; //没有立刻分配空间,int默认元素为0
(3)在定义时明确指定数组大小,且对数组进行一定的初始化
int temp2[5] = {1,2};
注意:
- 静态数组定义时,不能使用变量
int a[m] = {};//这是错误的写法
2.1.4数组的其他重要知识
(1)数组名,这个变量所占用的内存空间中,存放着数组首地址(同时也是第一个元素的地址)
printf("%p\n", &score[0]);
printf("%p\n", score);
这两句话的效果相同,其中%p专门用来打印地址,而“&”则是代表取变量的地址
(2)数组与循环的关系较为密切
①一般情况下, 对于将某些元素逐个装入数组中的过程,会使用数组+循环的组合
②另外,如果是两个字符串的对比,实际上也是将字符串放入数组中,依靠循环进行比较(比如下文中会提到的猜数字游戏,就是这个道理)
3.技术的具体应用
接下来主要谈一谈标题所提到的数组的实际应用的思路,具体实现会放在下一部分
3.1 应用1——进制转换
(1)数组的主要应用:记录计算结果,并输出
(2)程序思路:
该程序的目的是将任意的十进制数,转换为其他进制数,并输出
如十进制中的数字20,转换为二进制形式,为10100
具体算法根据如下:
由图可见,转换结果为10100
我们要做的,就是把这个10100放入数组中。
但是按照这个算法得到数字的顺序是00101,(即数组的第一个元素放的是数字的最后一位)因此输出的时候必须倒过来输出(如何倒过来输出后面再说)
3.2 应用2——游戏1·最后谁留下来
效果如图:
(1)数组的主要应用部分:记录每个人的编号、是否被淘汰
(2)游戏过程:
先输入一个淘汰号码,每个人从1开始按顺序依次喊出号码,喊出淘汰号码的人便会被淘汰,下一个人从1开始重新喊
该程序的作用便是能够通过不断循环遍历数组,逐次找出被淘汰的编号,以得出最后的生还者
(3)代码思路:
①将每个人的编号记录在一个数组中,然后设置两个变量,一个记录数组下标,一个记录喊出的号码
②两个数同时增加,当喊出的号码等于淘汰号码时,将对应的编号设置为' * '(星号),然后重置喊出的号码(赋值为1),
③重复上述内容,当检测到数组中内容为星号时,跳过该次循环(因>为星号代表已经被淘汰)
3.3 应用3——游戏2·猜数字
先放效果图:(1)游戏规则:
①系统会随机生成四个不同的数字,玩家需要猜测分别是什么
②玩家允许对正确答案进行猜测,对于玩家的猜测,系统会给予一定的反馈
③反馈格式为:mA,nB
其中m为A的个数,n为B的个数,A表示位置和数字都同时猜对的数字个数,B表示数字猜对了但是位置不对的数字个数
④玩家一共有10次机会,需要不断根据反馈调整自己的答案,以找出正确的序列
(2)数组的主要应用:
数组在这个程序中,主要是用来存放两个序列(正确序列与猜测序列),然后比较两个序列中的值,并返回A与B的个数
4.实际应用(具体实现步骤)
三个例子,我们一个个分析
4.1进制转换的实现
(1)代码细节:
int result[20] = {};
int i = 0;//从第一个元素开始,而且最后i可以反映数组中有多少个元素
逐步分析代码:
①首先定义一个数组result[20]以及变量“i”,这个变量i十分重要,不仅可以在下面展示while循环中起到记录下标,其最终结果,还能展现出进制转换后的数字的位数(因为我们不知道转化后的数字有多少位,可以靠i来获取)
//开始将转换后的数字按照逆序放入数组中
while (num != 0) {
result[i] = num % system;//把余数依次放在数组中
num /= system;//被除数要更新为商
i++;
}
这个while循环是由辗转相除法得来的,system指的是进制数,num为待转换的数
result[i]依次装入num对system取余的结果
//开始输出
printf("该数字的%d进制为",system);
for (i--; i >= 0; i--) {
if (result[i]>=10) {
printf("%c", result[i]+'A'-10);
}
else {
//0到9的数字直接输出即可
printf("%d", result[i]);
}
}
printf("\n");
最后输出即可,需要注意的是,由于数组中是按照逆序存储的最终结果,因此得倒过来输出
而最终结果的长度,就可以看上文所说的变量i
- for循环中的第一个i--是因为上面额代码i多++了一次
- if(result[i]>=10)主要是针对10以上的进制,如十六进制,这些进制会出现10以上的一些数字,并用大写字母代替,而printf("")中的result[i]+'A'-10,则是将数组中大于等于10的数字,转化为对应的英文大写字符(根据ASCII码)
4.2游戏1·最后谁留下来
(1)代码细节:
int nowNum = 1;//当前喊出来的号码
int lastNum = 7;//剩余人数
int totalNum = 7;
int i = 0;
//1.固定7个人游戏,每个人编号分别是1到7
char order[7];
for (int i = 0; i < 7; i++) {
order[i] = i + '1';
//printf("%c ", order[i]);
}
①先定义一些变量,然后定义一个char类型的长度为7的数组,并逐个赋值'1','2',‘3‘ .......‘7’
使用char类型的主要原因是后面想要通过*星号来代表被淘汰的人(当然用int类型数组,然后用0表示淘汰也行)
②然后就是对结果进行验算:
//2.对结果进行验算
while (lastNum > 1) {
//此时喊出号码,并判断是否为死亡号码
if (i == totalNum) {
//如果已经到头了,回到开头
i = 0;
}
if (order[i] == '*') {
//已经死亡了,直接下一个
i++;
continue;
}
else if (nowNum == dieNum) {
order[i] = '*';
lastNum--;//剩余人数减1
nowNum = 1;
//淘汰一个人就输出一次
for (int j = 0; j < 7; j++) {
printf("%c ", order[j]);
}
printf("\n");
}
else {
//否则继续喊出号码
nowNum++;
}
i++;
}
③其中lastNum表示剩余人数,totalNum表示总人数(这里其实是数组的长度,不会变化,该变量主要用于下标到6之后,能够通过if语句重新回到0)
④nowNum指的是当前喊出的号码,dieNum表示淘汰号码,当二者相同时,表明该选手被淘汰,则有:
if (nowNum == dieNum) {
order[i] = '*';
lastNum--;//剩余人数减1
nowNum = 1;
//淘汰一个人就输出一次
for (int j = 0; j < 7; j++) {
printf("%c ", order[j]);
}
printf("\n");
}
被淘汰的人用星号表示,下一次星号,则只能下标++,nowNum(喊出的号码)不会变化,即:
else {
//否则继续喊出号码
nowNum++;
}
另外,淘汰号码可以通过手动输入的方式进行更改:
printf("请输入淘汰号码:");
scanf_s("%d", &dieNum);
whoDie(dieNum);//淘汰游戏函数
4.3游戏2·猜数字
(1)代码细节:
int correctOrder[4];//放正确答案
int guessOrder[4];//放猜测的
srand((unsigned)time(NULL));//用来生成随机数的种子
int num = 0;
int numA = 0;
int numB = 0;
int totalTime = 10;//总共10次机会
①先定义变量
②然后下面开始制作正确的序列:
//1.准备好正确的数组序列
for (int i = 0; i < 4; i++) {
num = rand() % 10;//0到9随机取一个
for (int j = 0; j < i; ) {//保证四个数字不重复
if (num == correctOrder[j]) {
num = rand() % 10;
j = 0;
}
else {
j++;
}
}
correctOrder[i] = num;
//printf("%d ", num);//测试用,打印看一下
}
其中
for (int j = 0; j < i; ) {//保证四个数字不重复
if (num == correctOrder[j]) {
num = rand() % 10;
j = 0;
}
else {
j++;
}
}
该for循坏主要效果是,将前面已经定好的数字与这次随机数产生的数字进行比较,如果有相同的,则重新去随机数
③获取猜测序列:
//3.输入猜测序列
printf("输入你的猜测序列:\n");
while (site != 4) {
scanf_s("%d", &num);
guessOrder[site] = num;
site++;
}
这里虽然是循环4次,每次只获取1个,但是可以一次性输入四个(中间要有一个空格)
④计算A和B的数量:
numA = numB = 0;//重新从零计算
for (int m = 0, j = 0; m < 4; m++) {
if (correctOrder[m] == guessOrder[m]) {
//看看有没有A
numA++;
}
else {
//看看有没有B
j = 0;//重新从头比较
for (; j < 4; j++) {
if (m == j) {
//没有A,所以i和j相同的时候跳过
continue;
}
else if (correctOrder[m] == guessOrder[j]) {
numB++;
break;//B数目+1之后就退出循环
}
}
}
}
printf("%dA,%dB\n\n", numA, numB);
计算思路为:
将两个序列(正确的与猜测的)对齐,然后先看第一个下标的元素,将正确的元素与猜测的相比,如果正确,则numA++
如果不正确,则开始找有没有B,方法就是讲正确的第一个下标元素,以此与猜测序列后面的数字相比较,有相同的则numB++并停止循环
后面的同理,先比较相同位置,看有无A,没有的话再看有无B
⑤最后判断是否达成结束游戏的条件
//5.判断是否结束游戏
if (numA != 4) {
totalTime--;
if (totalTime == 0) {
printf("机会已经用光\n");
exit(EXIT_FAILURE);
}
else {
//机会还没有用光,但是还没有正确
printf("还有%d次机会,请继续", totalTime);
}
}
先用if判断numA有没有4个(有就证明已经猜对了)
然后在让totalTime--,并判断机会用光了没有
⑥
从第③步到第⑤步,是个循环(毕竟有多次机会),需要用while套在外面,循环条件则是numA != 4
while (numA != 4) {
site = 0;//每次重新赋值
//3.输入猜测序列
printf("输入你的猜测序列:\n");
while (site != 4) {
scanf_s("%d", &num);
guessOrder[site] = num;
site++;
}
//4.下面开始计算A和B的个数
numA = numB = 0;//重新从零计算
for (int m = 0, j = 0; m < 4; m++) {
if (correctOrder[m] == guessOrder[m]) {
//看看有没有A
numA++;
}
else {
//看看有没有B
j = 0;//重新从头比较
for (; j < 4; j++) {
if (m == j) {
//没有A,所以i和j相同的时候跳过
continue;
}
else if (correctOrder[m] == guessOrder[j]) {
numB++;
break;//B数目+1之后就退出循环
}
}
}
}
printf("%dA,%dB\n\n", numA, numB);
//5.判断是否结束游戏
if (numA != 4) {
totalTime--;
if (totalTime == 0) {
printf("机会已经用光\n");
exit(EXIT_FAILURE);
}
else {
//机会还没有用光,但是还没有正确
printf("还有%d次机会,请继续", totalTime);
}
}
}
printf("恭喜你猜对了!\n");