上两行word上下两行对不齐猜数字

上两行,下两行,相合互并为平行。(猜数字)_百度知道
上两行,下两行,相合互并为平行。(猜数字)
我有更好的答案
其他类似问题
为您推荐:
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁#include &stdio.h&
// 引入随机函数srand()、rand()所在的头文件
#include &stdlib.h&
// 引入时间函数time()所在的头文件
#include &time.h&
// 引入字符查找函数strchr()所在的头文件
#include &string.h&
int main()
// 定义保存目标数字的字符数组
char bull[5] = "";
srand((int)time(0));
// 利用rand()函数产生一个四位随机数,
// 并利用sprintf()函数将其转换成字符串,保存在bull字符数组中
sprintf(bull,
"%d", rand()%10000);
// 定义表示猜测数字的字符数组
char cow[5] = "";
// 定义表示猜测状况的A和B
int A = 0;
int B = 0;
// 猜测次数,最开始是第一次
int count = 1;
// 构造一个循环结构,只要没有完全猜对,
// 就继续下一次猜测
while(4!=A)
// 判断是否超过次数限制
// 最多猜测10次
if(count & 10)
puts("sorry,you lost. :");
// 跳出猜测循环,结束游戏
// 输出当前次数
printf("%d :",count);
// 获取用户输入的猜测数字
// 因为需要与目标数字进行比对,
// 所以将输入作为字符串(%s),而不是作为一个整数(%d)
scanf("%s",cow);
// 采用循环结构,逐个字符比对输入数字(cow)和目标数字(bull)
for(int i = 0; i&4; ++i)
// 判断当前字符是否相同
if(bull[i] == cow[i] )
// 如果相同,则表示数字正确且位置正确,
// A的值增加1
// 否则,判断当前数字是否是正确数字
// 在目标字符串中查找当前字符
char* p = strchr( bull, cow[i]);
// 如果p不是一个空指针(C语言中用NULL或者0表示),
// 则表示目标字符串中有这个字符,当前字符是正确数字,
// B的值增加1
if(0 != p)
// 输出此次猜测的结果
printf("%s : %dA%dB\n",cow,A,B);
// 如果完全猜测正确,则输出最终结果,结束游戏
if(4 == A)
printf("you win! the bull is %s.",bull);
// 否则,开始下一次猜测,次数增加1,A和B数据归零
&  &&陈良乔 ,《C程序设计伴侣》,人民邮电出版社,2012年10月,p39~42
  对于初学者来说,这个题目有一定难度和挑战性。但也正因为如此,从中更能发现初学者常犯的一些毛病。
  从整体上看,整个源程序只有一个main()函数,没有其他的自定义函数。这是初学者常见的一个毛病&&一main()到底。(参见拙著《品悟C》 第7章 问题4 &将main()函数进行到底&,P253)
#include &stdio.h&
// 引入随机函数srand()、rand()所在的头文件
#include &stdlib.h&
// 引入时间函数time()所在的头文件
#include &time.h&
// 引入字符查找函数strchr()所在的头文件
#include &string.h&
&  这种注释风格有两个特点:第一丑陋凌乱;第二浪费铺张,是一种把散文&膨化&成诗歌格式的作风。用这种格式投稿,不但可以劳累读者眼球还可以扩张版面多赚稿费。但如果在工作这样写代码,同事会骂你,老板会K你。对比一下下面的写法,相信不难看出故意&膨化&的代码是否丑陋。&
#include &stdio.h&
#include &stdlib.h&
// 引入随机函数srand()、rand()所在的头文件
#include &time.h&
// 引入时间函数time()所在的头文件
#include &string.h&
// 引入字符查找函数strchr()所在的头文件
int main()
&  main()的这种写法因为与现代C语言的精神相违背,因此不被提倡。较好的写法是
int main ( void )
// 定义保存目标数字的字符数组
char bull[5] = "";
&  这两行代码同样有浪费篇幅之嫌。bull作为程序的主要数据结构,选择字符数组并不明智。代码作者选择这种结构的目的是为了使用现成的库函数strchr()。使用现成的库函数无可指责,但在这个问题中字符数组这种数据结构会引起其他方面的问题。利弊相权,并不合算。
  此外这里的5是一个MAGIC NUMBER。
  这段代码的另一个问题是对数组毫无意义地进行初始化,这是一种无聊的行为。
srand((int)time(0));
  这一句代码的目的是设置种子数(seed)。这行代码虽然很短,但有很多潜在的问题值得讨论。
  首先time(0)这个写法,我的看法是属于C++风格,非常不C。在C语言中应该写time(NULL)。不过也有人认为在C代码中写time(0)无伤大雅。这可能是一个见仁见智的问题。
  另外一个问题是关于(int) time(0)这个表达式的,这个问题则并非是见仁见智那么简单。由于srand()的函数原型为:&
void srand (unsigned int);
&  因此&&&&&&&&&&
srand((int)time(0));
&在本质上其实就是
srand((unsigned)(int)time(0));
&  这是因为根据C语言规则,编译器总会在这里进行隐式类型转换。与下面两种写法比较一下&
srand((unsigned) time(NULL));
srand(time(NULL));
&  就会发现(int)这个类型转换相当地无聊,它徒劳地进行了一次毫无意义的、无谓的类型转换,但最终还是要转换为unsigned类型。就像歌里唱的那样:&终点又回到起点&,但代码作者还没&发觉&。因此这是一种画蛇添足的行为,说明了代码作者语法不清、逻辑混乱。
  (int) time(0)中的这个类型转换还存在其他一些问题。我们都知道,time()函数的返回值的类型为time_t,C语言并没有完全明确这种类型,只要求它是一种实数类型(real type)并且能够表示相应的时间。实数类型包括整数类型和实浮点类型,具体选择哪种类型由编译器自行确定。
  当time_t为浮点类型时(说实话没见过,但这种可能性是存在的),如果time(0)的返回值超出了所要转换成的整数类型的表示范围时,代码尽管可以通过编译,但程序实行的却是一种未定义行为(UB,undefined behavior)。
  由于time(0)的返回值为一正数,而&int类型所能表示的最大正整数显然小于unsigned类型所能表示的最大正整数,因此对time(0)进行(int)这样的转换显然比进行(unsigned)转换存在更大的发生UB危险性。
  如果time_t为整数类型(多数的编译器中是long类型),在进行long =& int 的转换时,如果被转换的值在int的表示范围内,不会有任何问题。但是如果被转换的值不在int类型表示的范围内时,结果有两种可能:implementation-defined或者是引发一个implementation-defined的信号(signal),前者意味着可移植性很差,后者则意味着程序被中断或挂起等待进一步的处理,这简直是在自寻烦恼、自讨苦吃。但是如果是进行long =& unsigned的转换,则根本不存在这些问题。
  因此,如果你追求代码的至简,这行代码应该写为
srand(time(NULL));
  如果你追求清晰和明确,则应该写为&
srand((unsigned) time(NULL));
// 利用rand()函数产生一个四位随机数,
// 并利用sprintf()函数将其转换成字符串,保存在bull字符数组中
sprintf(bull,
"%d", rand()%10000);
  这几行代码有两个问题。
  头一个,不清楚代码作者为什么要把sprintf()函数调用掰成了两行,这个函数调用并不长,完全没有任何必要掰成两行写。估计这又是&膨化&作风在作妖搞怪。
  第二个问题是,rand()%10000得到的并非是一个&四位&整数,它也有可能得到一位数、两位数或三位数,因此这行代码是错误的。
  也就是说,这个程序可能生成一个非四位数,然后让你按照四位来猜。更通俗一点说,这个游戏程序会&耍赖&,你可能绝对猜不出它的那个自称&四位数&的伪&四位数&。
  对于有些读者来说,这一点可能看起来不明显。其实你只要把第32行代码修改为
puts("sorry,you lost. :,原来的数为:"); puts(bull);
&  然后再运行程序就清楚了。我的一次运行结果是
    1 :1111
&&    B
    2 :1222
    3 :2122
    4 :3133
    5 :4244
    6 :4144
    7 :5155
    8 :6166
    9 :6177
    10 :6188
    sorry,you lost. :,原来的数为:
&&&&&   619
  看到这个结果了吧,你永远也猜不中。这样会耍赖皮的程序让人会感到索然无趣。
  由于不满足任务的基本功能要求,至此已经可以判定这是一个错误的程序了。这指东打西的程序是一种根本上的错误。后面的讨论与程序的正确性无关,纯粹是为了分析代码中的其他毛病。
  事实上生成一个四位伪随机数是一个简单的小学数学问题。由于四位正整数一共有个,所以可以先用rand()生成一个[,0]区间的伪随机数,即
  rand()%()
  然后再加上1000得到的就是四位伪随机数:
  rand()%()+1000
  这样产生的伪随机数能够保证一定是4位正整数,但是还不能确保一定是&没有重复数字的4位数&,这个问题将在后面解决。需要说明的是这个问题在样本代码中同样存在。
// 定义表示猜测数字的字符数组
char cow[5] = "";
&  这种数据结构的选择和bull是一脉相承的,两者同样不甚合理。这里的初始化同样是画蛇添足。
  这个数组的定义的位置也是不恰当的,它应该定义在后面的while循环语句的内部。理由是它只在那个语句的内部被用到。定义变量的原则是在哪儿用就定义在哪儿,尽量局部化。这就像不应该把卫生间用的卫生纸摆在客厅一样,是同一个道理。
// 猜测次数,最开始是第一次
int count = 1;
  这个变量的定义没有问题,但是将其初始化却非常蹩脚,蹩脚的原因与代码作者在后面选择的一个蹩脚的循环结构相关。下面是这个蹩脚结构的注释:
// 构造一个循环结构,只要没有完全猜对,
// 就继续下一次猜测
// 判断是否超过次数限制
// 最多猜测10次
&  从中不难发现样本代码作者思路混乱、前后不一。前面摆出了一副无限循环的架势,后来又说&最多猜测10次&。在这种混乱的思路指导下得到的代码必然丑陋不堪。
int A = 0;
while(4!=A)
&  站在一定的高度来看这段代码就会发现它的可笑之处,因为程序执行到第26行时A的值只可能为0而永远不可能为4。因而while(4!=A)虽然显得一本正经但其实却是煞有介事的装模作样。
// 判断是否超过次数限制
// 最多猜测10次
if(count & 10)
puts("sorry,you lost. :");
// 跳出猜测循环,结束游戏
  从这段代码来看,其实while语句完成的只是一个for语句的功能。代码的结构应该这样:
for ( count = 0 ; count & 10 ; count ++ )
puts("sorry,you lost.");
  把这结构和样本中的
while(4!=A)
if(count & 10)
这个结构对比一下,就会发现样本代码结构的蹩脚可笑十分明显。&
scanf("%s",cow);
&这行代码存在潜在的安全问题。由于cow的类型是char [5],所以在执行这条语句时用户一个手滑、多输入了一个字符就可能导致灾难。
如果实在要用scanf()完成输入,至少应该写成:
scanf("%4s",cow);
// 采用循环结构,逐个字符比对输入数字(cow)和目标数字(bull)
for(int i = 0; i&4; ++i)
// 判断当前字符是否相同
if(bull[i] == cow[i] )
// 如果相同,则表示数字正确且位置正确,
// A的值增加1
// 否则,判断当前数字是否是正确数字
// 在目标字符串中查找当前字符
char* p = strchr( bull, cow[i]);
// 如果p不是一个空指针(C语言中用NULL或者0表示),
// 则表示目标字符串中有这个字符,当前字符是正确数字,
// B的值增加1
if(0 != p)
  这段代码膨化得非常严重,简直可以称得上是膨化主义的典范。如果把它做一下&缩水&处理,剩下的干货连一半行数都用不到。  
  由于前面bull数组中可能得不到正确的数字(不是四位数的情况),所以这段代码没有意义。因为谁也不可能在一种错误的数据的基础上得到正确的算法。
&&&&& 即使不存在前面的错误,这段代码在逻辑上也是错的。例如当bull表示1234,而用户输入的cow是1122时,这段代码输出结果将是1A3B,而按照游戏规则,结果应该是1A1B。(感谢 王爱学志 网友指出)
  抛开正确性问题不谈,把如此臃长的结构塞在一个while循环语句内部也是极其丑陋的,很显然,尽管代码作者口口声声大喊大叫地嚷着似是而非的&搭积木&的口号,但其实根本尚未领悟结构化程序设计思想。这里的功能其实应该用一个函数实现。
// 如果完全猜测正确,则输出最终结果,结束游戏
if(4 == A)
printf("you win! the bull is %s.",bull);
  这段中的&4==A&这种把常量写在==前面的写法,感觉矫情得有些变态。
  此外,语句不如直接写return 0;。
  至此,样本代码评析完毕,下面进行重构。(未完待续)
续文链接:
阅读(...) 评论()产生一个神秘数字& & &接下来,我们需要产生一个神秘数字。Rust在它的标准库中还没有包括随机数函数。然而,Rust团队确实提供了一个一个rand crate。‘crate’是Rust代码的一个包。我们已经构建了一个crate,是可执行的。rand是一个库crate‘library crate’,包括可以被其他使用的代码。& & &使用外部crate是Cargo真正的闪光点。在我们可以使用rand写代码之前,我们需要修改我们的Cargo.toml。打开该文件,并在底部添加下列代码:
[dependencies]
rand=&0.3.0&& & &Cargo.toml文件的[dependencies]节跟[]节一样:所有跟随在它之后的都是它的一部分,直到下一节的开始。Cargo使用dependencies节得知你需要什么样的外部crate和需要什么版本的。在该例子中我们使用0.3.0版本。Cargo知道语义化版本,语义化版本是一个书写的标准。如果要使用最新版本,我们可以使用*或我们使用一个版本边界。Cargo的文档包含更多细节。& & &现在,无需修改我们的代码,让我们构建我们的:
$ cargo build
Updating registry `/rust-lang/crates.io-index`
Downloading rand v0.3.8
Downloading libc v0.1.6
Compiling libc v0.1.6
Compiling rand v0.3.8
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)& & &(你可能会看到不同的版本)& & &有许多新的输出!既然我们有一个外部依赖,Cargo从registry获取最新版本的所有东西,从Crates.io处的数据拷贝。Crates.io是在Rust系统中的人们提交他们可供他人使用的开源项目的地方。& & &更新完registry之后,Cargo检查我们的[dependencies]并下载我们还没有的东西。在这里,尽管我们说我们依赖于rand,我们也抓取了libc的拷贝。这是因为rand依赖于libc来工作。下载它们之后,编译它们,最后编译我们的项目。& & && & &如果我们再次运行cargo build,我们将会得到不同的输出:
$ cargo build& & &这是对的,没有输出!Cargo知道我们的项目已经被构建,它所有的依赖都已经构建,所以没有理由再做这些工作。无事可做,它简单退出了。如果我们打开src/main.rs,做一个不重要的修改,保存,我们将会看到一行:
$ cargo build
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)& & &我们告诉Cargo我们需要任何0.3.x版本的rand,所以它取回了当前的最新版本v0.3.8。但是下个星期会发生什么呢,版本v0.3.9出来了,修复了一个重要bug?修复bug是重要的,如果0.3.9包含退化,破坏我们的代码怎么办?& & &这个问题的答案是在你的工程目录里你将会找到Cargo.lock文件。但你第一次构建你的工程时,Cargo指出所有适合你条件的版本,并把它们写回Cargo.lock文件。当你未来构建你的项目时,Cargo将会看到Cargo.lock文件存在,并会使用特定的版本,而不会做些再次指出版本的工作。这允许你可以重复的自动创建。换句话说,我们将会停留在0.3.8版本,除非我们明确地升级,对于共享我们代码的任何人都是这样的,感谢lock文件。& & &当我们想使用v0.3.9时怎么办呢?Cargo有另外一个命令,,意思是说‘忽略lock文件,找出所有的适合我们知名的所有最新版本。如果它们好使,就把它们吸出到lock文件’。但是,默认情况下Cargo只会查找大于0.3.0小于0.4.0的版本。如果我们想使用0.4.x,我们不得不直接修改Cargo.toml。当我们这么做了,下一次我们cargo build时,Cargo将会升级索引并重新评估我们的rand需求。& & &对于Cargo和它的‘生态系统(eco)’要说的还有很多,但不是现在,我们已经知道了需要知道的。Cargo使重用库变的很容易,所以Rustaceans趋向写一些小程序,这些小程序会包含一些子包。& & &让我们继续使用rand。这是我们的下一步:
use rand::R
fn main() {
println!(&Guess the number!&);
let secret_number = rand::thread_rng().gen_range(1, 101);
println!(&The secret number is: {}&, secret_number);
println!(&Please input your guess.&);
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect(&failed to read line&);
println!(&You guessed: {}&, guess);
}& & &我们做的第一件事情是改变第一行。现在是extern crate rand。因为我们在我们的[dependencies]定义了rand,我们可以使用extern crate让Rust知道我们将会使用它。着等价于use rand;所以我们可以通过它的前缀rand::使用rand中的所有东西。& & &接下来,我们增加了另外一个use行:use rand::Rng。在某刻我们将会使用一个方法,该方法需要Rng在作用域内。基本的思想是:方法定义在称作‘traits’的上面,为了是方法正常工作,它需要‘trait’在作用域内。更多细节,查阅traits节。& & &我们中间我们增加了额外的两行:
let secret_number = rand::thread_rng().gen_range(1, 101);
println!(&The secret number is: {}&, secret_number);& & &我们使用rand::thread_rng()函数获得随机数生成器的一个拷贝,该生成器定位到我们执行的本地特定线程。因为我们上面使用rand::Rng,它有一个gen_range()方法可用。这个方法接受两个参数,并产生一个介于它们之间的数。包括下界,不包括上界,所以我们需要1和101产生一个1和100之间的数。& & &第二行仅仅打印出神秘数字。在我们的开发程序时是有用的,这样我们可以容易测试。但是在最终版本我们会删掉它。如果在你启动的时候它打印出了答案,它将不再是一个。& & &花一些时间运行我们的新程序:
$ cargo run
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
Running `target/debug/guessing_game`
Guess the number!
The secret number is: 7
Please input your guess.
You guessed: 4
$ cargo run
Running `target/debug/guessing_game`
Guess the number!
The secret number is: 83
Please input your guess.
You guessed: 5& & &非常好!下一步:让我们猜测的数字跟什么数字做比较。&&国之画&&布布分享&&&& &&&&
版权所有 京ICP备号-2
迷上了代码!

我要回帖

更多关于 猜数字游戏下载 的文章

 

随机推荐