前言
这道题目,映入眼帘的一瞬间,似曾相识,这种感觉非常的强烈。可是,问题就在于,具体细节又回想不起来,怎么也想不起来,究竟在哪里遇到过。这种心情,非常的难受。甚至去翻看书的前几章,越想找越找不到,真的非常难受。
绞尽脑汁,回想了好半天,想起来,好像是这个猜数游戏可以在不超过10次的情况下,就把答案猜出来。再具体的,怎么也想不起来了。也不知道怎么回事,在这儿就僵持住了。
后来,等把这道题目解决了,无意间,重新翻看本书,竟然真的找到了。
为了不让大家再有这种似曾相识,却怎么也想不起来的经历。我决定,在开篇就把书中曾经提到过的地方贴出来。
其实,早在书中第1章,1.10节,在Windows、Linux和Mac OS上测试并运行一个C应用程序,其中的示例就是本道题目。
书中这段话,提到了,游戏本身并不对你可以猜测的次数设置限制,但是肯定能够在不超过10次的猜测中猜对这个范围内的任何一个数。然后,提到了第5章的练习题。甚至,提到了在这个游戏中蕴含有某些计算机科学的奥秘——在“6.10节数组查找”中将学会“二分查找”技术。
回过来,再看一下5.32和5.33这两道题目。
本以为,书中曾经提到过的地方,会出现在第3章或是第4章,因为这两章讲了判断和循环,真没想到在第1章。越往后学习,对书中前面章节的印象会逐渐不清晰。而且,越往后面学,时不时翻到前3章,再看书中的话,会有不一样的感受。第一遍学习的时候,觉得那只是几句概念性的话。现在再看,内心似乎有了一种更深层次的感悟,更深一层的知道了这些话在表述什么。
OK,回顾就到此,我们接下来,结合前面学习的章节,尝试解决这个道题目。
这里,可能会有小伙伴儿问到,既然都已经知道书中第1章就给出了示例代码,直接拿来用呗,还想啥呀。
怎么说呢,学习编程,还是要把心静下来,我们需要的不是现成的答案。如果,只是单纯的解决这道题目,至少不是我现在想要的。我现在想要的,是通过章节知识点的学习,通过练习题,借此来激发自己思考问题、解决问题的能力。培养能力的过程,是异常痛苦、煎熬、绝望、孤独和漫长的。可是,一旦把能力培养起来,我们就可以去运用了。我们每次都会遇到不同的问题,我们不可能每次都有现成的答案,这时候就必须具备能力去思考、解决问题。
你可以觉得恐惧,更可以觉得麻烦,这是人正常的情感反馈。可是,你不能因此而灰心丧气,甚至停下探索的脚步。不然,能力就无法突破,长期来看,这才是真正绝望的。好了,废话不多说,步入正题。
思考
其实,这两道题目本身,结合前面章节所学,并不难。里面牵扯到的点:随机数、判断比较、循环、输入等等,这些都是前面章节所学的知识点。
真正把我难住的地方:
其一,当时,感觉在书中曾经遇到过这个问题,可是怎么也回想不起来,更找不到具体在书中哪里。就此,心态出了问题,情绪非常纠结,以至于完全没心思去思考这道题目。
其二,即使后面尽可能调整了情绪,稳定了心态,还是感觉烦烦的。以至于,这道题一共涉及两处输入,一处是开始的猜数输入,另一处是猜对数后,输入’y’或’n’。针对这两处输入,选用scanf()函数或getchar()函数,这两个函数有什么区别,彻底混乱掉了。同时,关于输入’y’继续新一轮的猜数游戏,对应的循环语句,要如何设计,同样彻底陷入了混乱。
总之,在解决这道题目的始终,我一直耿耿于怀。印象中,在书里遇到过这个问题,究竟在哪儿遇到的呢,为什么就是找不到呢。
这从表面上看,是一次糟糕的经历。反过来,也是意外收获。通过这道题目,我不仅解决了题目本身。同时,我还知道了,今后在解决问题时,可能还会遇到这种似曾相识,却怎么也找不到具体细节的情况。这个时候,还会选择像这次一样吗,陷入深深的纠结之中,以至于完全无法用正常的思绪去解决眼前的问题。已经经历过一次了,还会选择这样吗。想到这里,我想说,这道题目带给我的,已经远远超过了题目本身。由此可见,心态真的很重要。
解决
当时,在苦苦找寻不到究竟在书中什么地方遇到过这个问题,我甚至去询问了chatgpt,确实得到了一种答案。可是,我就是跟自己较上劲了,不想把chatgpt给出的答案直接拿来用,必须要有自己思考的一个过程出来,优先尽可能的借鉴书中过往示例中用到的方法。
于是,我立马联想到了,在书中第3章,3.9节的示例中,有类似的设计方法。
这个示例,在while循环语句外面先打印了一遍提示信息,在while循环体里面又打了一遍相同的提示信息。这个设计方法,可以借鉴到本题中,用在猜对数,输入‘y’后,开启新一轮猜数。
接下来,捋清思路,针对本道题目的两处输入,一处是开始的猜数输入,另一处是猜对数后,输入’y’或’n’,究竟使用scanf()函数还是getchar()函数。
首先,要从scanf()函数和getchar()函数本身出发了。
我们先来看看书中,第2章,2.3节,另一个简单的C程序:两个整数求和。在这一节,本书第一次讲解了关于scanf函数的相关概念,如下图:
我们再看书中,第4章,4.7节,switch多重选择语句。在这一节,本书第一次讲解了关于getchar函数的相关概念,如下图:
重点
为了方便,把书中相关篇幅的介绍,全部贴出来了。简单总结一下:
1.scanf函数和getchar函数,都来自<stdio.h>,均可从标准输入流读取,标准输入流通常是键盘。
2.scanf函数,有两个实参,第一个实参是格式控制字符串,用来指示用户将要输入数据的类型。第二个实参是以一个“与符号&”开始的,后面跟着一个变量名。符号&在C语言中被称为取地址运算符。
3.书中紧跟讲到了这样几句话:当执行上面的scanf语句时,计算机将等待用户为变量integer1键入一个数值。用户键入一个整数作为响应,然后按“回车”键(Enter key)(有事标为“Return”键)将这个整数发送给计算机。这里,需要格外注意,每次输入都会通过按“回车”键将这个整数发送给计算机。记住,这个按“回车”键的操作。
4.getchar函数,从书中的介绍来看,没有参数。
5.书中有这样的描述:getchar函数,(来自<stdio.h>)从键盘读入一个字符,并将其存储在整型变量grade对应的存储单元中。
6.字符通常是存储在类型为char的变量中。但是,由于字符在计算机中通常是用一个字节的证书来表示,所以C语言允许将字符存储在任何整型的变量中,这是C语言的一个重要特性。getchar函数就是把用户输入的字符当成一个整数。我们既可以将字符当成字符,也可以将字符当成整数,这要根据具体的应用情况来确定。
注意,第5点和第6点的描述,信息量很大,包含了很多隐藏的细节,如果忽略这些细节,会导致后续处理猜数游戏输入时,出现很多问题。到那时,再回过头来重新学习理解,体验会非常难受,这也是我本人踩的一个坑,花了很长时间,才把这个坑踩完。
现在分析,第一,getchar函数是从键盘读入一个字符,注意是字符型。scanf函数可以根据实参的格式控制字符串,控制输入的数据类型。第二,getchar函数会返回一个整型的值。第三,C语言允许将字符存储在任何整型的变量中,这是C语言的一个重要特性。第四,getchar函数返回的整型值,即是从键盘读入字符对应的ASCII字符集。不要小看这四点,里面蕴含的信息量很多,切记!
7.书中有一段关于处理“换行”、“tab”和“空格”字符的描述,这段描述和上述第3点一样,旨在处理“回车”键等字符,如下图。
因此,无论是scanf函数还是getchar函数,都需要注意“回车”键产生的字符,要对此进行处理。否者,程序就会错误读取“回车”键产生的字符。这一点,重中之重,稍不注意,就会导致猜数游戏输入逻辑出现错误。
而且,书中目前只介绍了switch函数如何处理“换行”、“tab”和“空格”字符,并没有针对scanf函数和getchar函数去直接说明如何处理同样的问题。这就需要认真阅读,找到解决办法。
解决
有了以上的内容作为基础,我们先使用scanf函数来实现:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int guess(void);
int main(void)
{
int a;
int b;
char again;
srand(time(NULL));
again = 'y';
b = guess();
printf("%s\n", "I have a number between 1 and 1000.");
printf("%s\n", "Can you guess my number?");
printf("%s\n", "Please type your first guess.");
scanf("%d", &a);
while (again == 'y')
{
if (a < b)
{
printf("%s\n", "Too low. Try again.");
scanf("%d", &a);
}
if (a > b)
{
printf("%s\n", "Too high. Try again.");
scanf("%d", &a);
}
if (a == b)
{
printf("%s\n", "Excellent! You guessed the number!");
printf("%s\n", "Would you like to play again (y or n)?");
scanf(" %c", &again);
if (again == 'y')
{
b = guess();
printf("%s\n", "I have a number between 1 and 1000.");
printf("%s\n", "Can you guess my number?");
printf("%s\n", "Please type your first guess.");
scanf("%d", &a);
}
}
}
}
int guess(void)
{
return 1 + rand() % 1000;
}
注意,上述代码,当两数相等时,使用了scanf(" %c", &again);。其中," %c",这里是有一个空格的,在代码块里可以更加清楚的看出来。这个空格的作用是针对scanf函数,让其跳过任意数量的空白字符,直到遇到真正的字符。这正对应了上述提到的,要处理按“回车”键产生的换行空白字符。
接下来,再使用getchar函数来实现:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int guess(void);
int main(void)
{
int a;
int b;
char again;
srand(time(NULL));
again = 'y';
b = guess();
printf("%s\n", "I have a number between 1 and 1000.");
printf("%s\n", "Can you guess my number?");
printf("%s\n", "Please type your first guess.");
scanf("%d", &a);
while (again == 'y')
{
if (a < b)
{
printf("%s\n", "Too low. Try again.");
scanf("%d", &a);
}
if (a > b)
{
printf("%s\n", "Too high. Try again.");
scanf("%d", &a);
}
if (a == b)
{
printf("%s\n", "Excellent! You guessed the number!");
printf("%s\n", "Would you like to play again (y or n)?");
getchar();
again = getchar();
getchar();
if (again == 'y')
{
b = guess();
printf("%s\n", "I have a number between 1 and 1000.");
printf("%s\n", "Can you guess my number?");
printf("%s\n", "Please type your first guess.");
scanf("%d", &a);
}
}
}
}
int guess(void)
{
return 1 + rand() % 1000;
}
注意,只是在猜对数,输入y或n时,更换为getchar函数。在其余输入数字的地方,依然选择使用scanf。这是因为,上述已经阐述过,getchar函数,(来自<stdio.h>)从键盘读入一个字符,该函数会返回一个整型数值,这个数值即是字符对应的ASCII字符集。因此,在输入数字时,getchar函数不支持。所以,选择scanf来控制输入数字。
在again = getchar();语句的前后,都增加了一句getchar();。这是因为,需要清除掉留在输入缓冲区里面的换行符,这个换行符是有按下“回车”键产生的。没错,正如上文所提到的,scanf函数在读取一个数字时,我们需要按下“回车”键,将其发送给计算机。于是,在输入缓冲区就会因为按下“回车”键产生换行符,需要对这个换行符进行处理。否则,again = getchar();语句就会读取留在输入缓冲区里面的换行符,导致跳出while循环。
如果,程序在getchar之前,多次调用了scanf,以实际的编译器运行效果看,并不会出现在输入缓冲区累加换行符。比如:在getchar语句前,调用了三次scanf,每次都按下了“回车”键。三次调用scanf后,在输入缓冲区留下的换行符并不是累加的三个,而是只有一个。这个结果是以当前的编译器实际的运行结果得出的,也就是说,第一次调用scanf后,在输入缓冲区留下的换行符,会在第二次调用scanf时,被跳过,因为scanf传入了%d,只识别整数型变量。这个跳过,以编译器实际的表现结果看,就是没有在输入缓冲区内进行累加。所以,只需要在again = getchar();前加上一句getchar(),来消耗掉输入缓冲区留下的一个换行符。这里的消耗掉,其本质是,getchar负责读取一个字符,把输入缓冲区留下的这一个换行符读取出来,输入缓冲区就没有这个换行符了,即消耗掉。
友情提示,这里,才是这道题目,真正有深度的地方。更进一步,了解了输入的一些细节。像是,输入缓冲区,换行符的处理等问题。可能比较绕,再加上本人能力有限,无法阐述的更加平易近人。我本人,也在这里卡了很长时间。总之,需要花一些时间和心思,静下心来,好好想清楚。失之毫厘谬以千里,这块是基础,如果出现毫厘之间差错,后续就会偏差出千里。
为了能够有个更加清晰的理解,我们不再以文字叙述为重,而是以代码来更加直观的感受。
第一段思考
先看下面一段代码
#include <stdio.h>
int main(void)
{
int a;
char c;
a = 70;
c = a;
printf("%d\n", a);
printf("%c\n", c);
}
其对应的运行结果,如下图:
是不是感觉有些混乱了,这段代码,我们先声明了一个整型变量a和一个字符型变量c。然后,整型变量a初始化为70,把整型变量a赋值给字符型变量c。最后,分别打印变量整型变量a、字符型变量c。结果,a为70,c为字符F。为什么c打印的结果是字符F呢。
因为,字符通常是存储在类型为char的变量中。但是,由于字符在计算机中通常是用一个字节的证书来表示,所以C语言允许将字符存储在任何整型的变量中,这是C语言的一个重要特性。这也是书中所讲的ASCII字符集。
是不是感觉更加混乱了呢,其实,也很简单,只需要记住:C语言允许将字符存储在任何整型的变量中,这是C语言的一个重要特性。
趁热打铁,以上面这个例子为基础,做个改变:
#include <stdio.h>
int main(void)
{
int a;
char c;
a = getchar();
c = a;
printf("a: %d\n", a);
printf("c: %c\n", c);
}
运行代码,我们输入字符F,查看打印结果,如下图:
我们看到,输入字符F,整型变量a打印结果为70,字符变量c打印结果为F。由此可见,getchar函数,会读取字符并返回该字符对应ASCII字符集中的整型数值。这更进一步说明了,C语言允许将字符存储在任何整型的变量中,这是C语言的一个重要特性。
在上面两个例子的基础上,在做一次改变:
#include <stdio.h>
int main(void)
{
char c;
c = getchar();
printf("c: %c\n", c);
}
运行代码,输入字符F,查看打印结果,如下图:
我们输入字符F,打印结果为字符型变量c的值为F。注意,getchar函数的返回值是整型的,变量c是字符型的。我们直接用字符型变量c,去接getchar函数的整型返回值,程序会自动进行转换。把整型的返回值,根据ASCII字符集,转换为对应的字符。因此,最终打印结果为F。又一次证明,C语言允许将字符存储在任何整型的变量中,这是C语言的一个重要特性。
到此,是否联想到,C语言中的数据类型转换呢,这两个概念是否重合或是混乱呢,让我们从长计议。
首先,在本书第3章,3.9节,自顶向下、逐步求精的算法 设计方案2:标记控制的循环,出现了数据类型转换的相关介绍,如下图:
至此,可以简单总结,数据类型转换是指不同类型的数据之间进行互相转换。
而C语言允许将字符存储在任何整型的变量中,这是C语言的一个重要特性。这中间也牵扯到两种数据类型,整型和字符型。看上去,也很像是数据类型转换。
其实不然,对于C语言中将字符存储在整型变量中的情况,并不属于数据类型转换。这是因为char类型在C语言中本质上是一个整型类型,因此可以直接将字符存储在char类型的变量中,而无需进行显式的数据类型转换。
在C语言中,字符类型char被设计为能够存储ASCII码中的字符,并且它的大小通常为1字节(8位)。由于char类型是整型类型,因此可以将字符直接存储在char类型的变量中,而无需进行任何额外的数据类型转换。
此外,C语言标准还允许将字符常量存储在其他整型类型的变量中,如int、short、long等。由于字符常量是整型常量,可以用整数形式表示,因此可以将字符常量赋值给其他整型变量。
是不是很有趣,本来以为只是几个普通的章节概念,只是一道小小的题目,竟然能够牵扯出如此多的细节信息,引发如此之多的思考,信息量如此之大。也难怪,会在这里踩坑,花费如此之多的时间。现在看来,都是值得的。另外,对“认真读书”这四个字,有了更深层次的认识,心中油然而生一种敬畏。
第二段思考
使用scanf函数和getchar函数
#include <stdio.h>
int main(void)
{
int c;
scanf("%c", &c);
while (c == 'y')
{
printf("now-c: %c\n", c);
scanf("%c", &c);
}
}
在这段代码中,我们不断的输入y,程序并不会执行while循环。
原因是,当在while循环外,第一次调用scanf,输入字符y,按下“回车”键。输入缓冲区有两个字符,第一个是字符y,第二个字符是“回车”键产生的换行符。没错,在C语言当中,换行符也属于字符。第一个字符y,通过scanf(“%c”, &c);读取给了字符型变量c。第二个字符即换行符,被留在了输入缓冲区。
当代码逻辑执行到while语句的判断条件时,字符型变量c的值是y,符合条件,进入while循环,先打印c的值为y。然后,出现了第二次调用scanf。此时,scanf并不会按照预期的那样,等待输入,而是会把第一次调用scanf后,留在输入缓冲区的换行符读入。至此,字符型变量c的值变成了换行符,不再等于y,不符合while循环的条件,跳出了循环,程序运行结束。
定位到了问题,就针对性改进一下:
#include <stdio.h>
int main(void)
{
int c;
scanf("%c", &c);
getchar();
while (c == 'y')
{
printf("now-c: %c\n", c);
scanf("%c", &c);
}
}
在while循环的外部,第一次调用scanf后,增加getchar();语句,单纯把留在输入缓冲区的换行符读取出来,并没有赋给任何变量,通过这种形式,把换行符从输入缓冲区消耗掉。
此时,运行代码,效果如下:
当不断的输入y,程序并没有一直循环下去。分析一下原因,首先while循环外部,scanf函数已经对应增加了getchar来消耗掉输入缓冲区的换行符,这块已经没有什么问题了。
接着,来看while循环内部。在while循环内部,第二次调用了scanf,在其后,也会产生换行符,该换行符也会留在输入缓冲区。如果,不对其进行处理,当进行下一次while循环时,scanf也不会按照预期等待新的输入,而是读取第一次进入while循环后,调用scanf,留在输入缓冲区的换行符。同理,在while循环内,调用scanf后,同样加上一句getchar,消耗掉输入缓冲区的换行符。代码如下:
#include <stdio.h>
int main(void)
{
int c;
scanf("%c", &c);
getchar();
while (c == 'y')
{
printf("now-c: %c\n", c);
scanf("%c", &c);
getchar();
}
}
如此,不断的输入y,程序会执行while循环。代码运行结果:
因此,这里,我们需要注意,getchar函数,是依次读取输入的字符。不像scanf函数,可以传参,比如%d,读取指定类型的数据。
这也是为什么,书中会把getchar放入while循环条件中,实现不断输入并打印其对应的结果,就是利用了getchar可以不断的读取输入的字符:
#include <stdio.h>
int main(void)
{
char c;
while ((c = getchar()) != EOF)
{
printf("%c", c);
}
}
如果,不把getchar放入while循环条件中,就需要如下的代码:
#include <stdio.h>
int main(void)
{
char c;
c = getchar();
while (c != EOF)
{
printf("%c", c);
c = getchar();
}
}
接下来,我们再来讨论另一种解题方法,这个是受到chatgpt启发,并对chatgpt给到的答案进行了优化,已经和chatgpt的原版答案有所区别。当我在看到这个答案后,我告诫自己,反倒要好好反思一下,什么叫学以致用,不能只是说说,要多动脑子想想。
直接上代码:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int guess(void);
int main(void)
{
int a;
int b;
char again;
srand(time(NULL));
again = 'y';
do
{
b = guess();
printf("%s\n", "I have a number between 1 and 1000.");
printf("%s\n", "Can you guess my number?");
printf("%s\n", "Please type your first guess.");
scanf("%d", &a);
do
{
if (a < b)
{
printf("%s\n", "Too low. Try again.");
scanf("%d", &a);
}
if (a > b)
{
printf("%s\n", "Too high. Try again.");
scanf("%d", &a);
}
} while (a != b);
printf("%s\n", "Excellent! You guessed the number!");
printf("%s\n", "Would you like to play again (y or n)?");
scanf(" %c", &again);
} while (again == 'y');
}
int guess(void)
{
return 1 + rand() % 1000;
}
这种写法,使用了do while循环语句。
之前使用while循环,需要先在while循环外部,打印一遍猜数的规则信息。然后,在while循环内部,当猜数正确时,再打印一遍猜数的规则信息。采用了书中第3章,3.9节的示例中,类似的设计方法。
do while循环语句,很好的解决了这个问题,只需要写一次猜数的规则信息即可。因为do while循环语句的特点,即先执行一遍循环体,再去判断循环条件是否满足。
其次,这种写法,使用了do while循环的嵌套,巧妙的实现了相应的功能。外层do while循环用来判断当猜数正确时是否继续,内层do while循环用来进行每次猜数的结果判断。需要注意的是,这种写法,在第一次遇到本题目时,需要扎实的基础知识、灵活的思考和相当的技巧,是有一定难度的。当然,如果你一开始就能想到此方法,也很不错哦,戒骄戒躁,继续努力。
当然,上述代码使用的是scanf,当然也可以改写为getchar,如下图:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int guess(void);
int main(void)
{
int a;
int b;
char again;
srand(time(NULL));
again = 'y';
do
{
b = guess();
printf("%s\n", "I have a number between 1 and 1000.");
printf("%s\n", "Can you guess my number?");
printf("%s\n", "Please type your first guess.");
scanf("%d", &a);
do
{
if (a < b)
{
printf("%s\n", "Too low. Try again.");
scanf("%d", &a);
}
if (a > b)
{
printf("%s\n", "Too high. Try again.");
scanf("%d", &a);
}
} while (a != b);
printf("%s\n", "Excellent! You guessed the number!");
printf("%s\n", "Would you like to play again (y or n)?");
getchar();
again = getchar();
getchar();
} while (again == 'y');
}
int guess(void)
{
return 1 + rand() % 1000;
}
同理,需要在again = getchar();语句前后加上getchar();来处理掉输入缓存区留下的换行符,原因同上,这里不再赘述。
到此,为什么会说,看到了这种新的写法,要反思学以致用。因为,我使用的是,书中第3章,3.9节的示例中,类似的设计方法。而书中第4章,4.8节讲来do…while语句,我没有能够想到用此法来解题,确实学的还不够,还需要继续努力。不能只停留在旧的解题思路上,对于新学习的知识也要能够实时运用。
好了,经过这一番折腾,终于是把这篇文章暂告一段落了。前前后后,自己踩坑了很多地方,花费了很多时间去思考,心情几度煎熬到崩溃,好在最后坚持下来,把遇到的一些问题想通了,谢天谢地。
要持续学习,自学,共勉。
补充一下:为什么1-1000,,随机一个数,不超过10次就可以猜出。每次猜数都采用二分法,2的10次方是1024约等于1000。比如,第一次猜500,如果显示结果为太小了,下一次猜数范围缩小为500-1000,接着猜750,如果还是太小了,参数范围变为750-1000。因此,每次猜数范围都以二分法进行分割,预计总次数可以在10次以内完成。
收尾
完成5.33道题目
两种写法,第一种,使用前几章示例中的设计方法,添加代码逻辑:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int guess(void);
int main(void)
{
int a;
int b;
int num;
char again;
srand(time(NULL));
num = 0;
again = 'y';
b = guess();
printf("%s\n", "I have a number between 1 and 1000.");
printf("%s\n", "Can you guess my number?");
printf("%s\n", "Please type your first guess.");
scanf("%d", &a);
while (again == 'y')
{
num += 1;
if (a < b)
{
printf("%s\n", "Too low. Try again.");
scanf("%d", &a);
}
if (a > b)
{
printf("%s\n", "Too high. Try again.");
scanf("%d", &a);
}
if (a == b)
{
printf("%s\n", "Excellent! You guessed the number!");
if (num < 10)
{
printf("%s\n", "Either you know the secret or you got lucky!");
}
if (num == 10)
{
printf("%s\n", "Ahah! You know the secret!");
}
if (num > 10)
{
printf("%s\n", "You should be able to do better!");
}
printf("%s\n", "Would you like to play again (y or n)?");
// scanf(" %c", &again);
getchar();
again = getchar();
getchar();
if (again == 'y')
{
b = guess();
printf("%s\n", "I have a number between 1 and 1000.");
printf("%s\n", "Can you guess my number?");
printf("%s\n", "Please type your first guess.");
scanf("%d", &a);
}
}
}
}
int guess(void)
{
return 1 + rand() % 1000;
}
添加一个标记值,在猜对数后,对标记值进行判断。
第二种写法,使用后来的do while嵌套:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int guess(void);
int main(void)
{
int a;
int b;
int num;
char again;
srand(time(NULL));
num = 0;
again = 'y';
do
{
b = guess();
printf("%s\n", "I have a number between 1 and 1000.");
printf("%s\n", "Can you guess my number?");
printf("%s\n", "Please type your first guess.");
scanf("%d", &a);
do
{
num += 1;
if (a < b)
{
printf("%s\n", "Too low. Try again.");
scanf("%d", &a);
}
if (a > b)
{
printf("%s\n", "Too high. Try again.");
scanf("%d", &a);
}
} while (a != b);
printf("%s\n", "Excellent! You guessed the number!");
if (num < 10)
{
printf("%s\n", "Either you know the secret or you got lucky!");
}
if (num == 10)
{
printf("%s\n", "Ahah! You know the secret!");
}
if (num > 10)
{
printf("%s\n", "You should be able to do better!");
}
printf("%s\n", "Would you like to play again (y or n)?");
// scanf(" %c", &again);
getchar();
again = getchar();
getchar();
} while (again == 'y');
}
int guess(void)
{
return 1 + rand() % 1000;
}
同理,添加一个标记值,在猜对数后,对标记值进行判断。