撰写了文章 发布于 2020-02-03 14:31:33
《程序编写 编程入门 - C/C++》(五)循环与判断
本系列收藏夹 https://cowlevel.net/collection/3932815
爱发电!https://afdian.net/@kingfeng新的课题
在先前,我们完成了我们的第一个程序,软件随机生成两个十以内的数字和一个加减符号,等待用户输入,并判断结果是否相符。
而现在,我们会在这个题目的前提上修改一下,加入乘除法、加入积分系统、同时我们会增加题目数量,最后再为我们的答题者记录分数以激励他学习。
判断
顾名思义,判断是用以一个特定的条件,一般是一系列代表着某个状态的变量,与期望值进行比较,并决定程序的走向。
If 结构
一个标准的if 结构如下
if(条件){
符合条件时执行的语句
}
其流程为
+
|
v
+---+--+
| if +--+
+---+--+ | Y
| v
N | +--+-----+
| | ...; |
| +--+-----+
| |
+<----+
|
v
当条件为真时,进入Y分支,并执行其中的语句,而后继续执行程序其他语句
判断条件
在If结构的判断中,判断条件会计算其表达式的值,其中表达式就是前文中的表达式。
在实际使用时,非常常见的例如对两个数字的比较
if(a == 1){
puts("A is 1");
}
来表示,当a的值与1相等时,执行puts()语句,即输出"A is 1"字符串
我们也可以用一个更长的表达式来确定这个值,例如
if((a < 5)&&(a > 0)){
puts("0 < a < 5");
}
使用了一个 && 运算符(逻辑与,两方都为真结果为真,否则为假)来判断a的范围。
也可以直接使用变量或者一个函数的返回值
if(a){
puts("a is not 0");
}
if(foo()){
puts("foo return true");
}
在C语言中,非零的数字其值都为真
If Else 结构
而当我们需要处理条件符合与不符合两种情况时,可以使用 if -else 结构
一个标准的if-else结构如下
if(条件){
符合条件时执行的语句
}else{
不符合条件时执行的语句
} 其流程为
+
|
v
+---+--+
+-+ if +-+
Y | +------+ | N
v v
+--+--+ +--+--+
| ..; | | ..; |
+--+--+ +--+--+
| |
+-----+----+
|
v
当条件符合是,执行Y分支的语句,否则执行N分支的语句。
if(a == 5){
puts("a is 5");
}else{
puts("a is not 5");
}
在a的值是5的时候,输出"a is 5"而在a的值不是5的时候,输出"a is not 5"
If - Else 嵌套结构
当我们的程序不满足于一种或者两种的时候,可以使用 if-else 的嵌套来实现多个分支
一个标准的if-else嵌套结构如下
if(条件A){
符合条件A时执行的语句
}else if(条件B){
不符合条件A,符合条件B时执行的语句
}else if(条件C){
不符合条件AB,符合条件C时执行的语句 }else{
不符合所有条件时执行的语句
}
需要注意的是,程序执行整个结构的过程中,只会进入第一个成功的条,并执行其中的语句,而后离开整个结构不进行后续判断
if语句允许省略花括号如
if(a == 0) puts("A is 0"); if(a == 2) puts("A is 2"); if(a == 5) puts("A is 5"); else puts("A is not 5");当省略了花括号后,if会影响其后的第一条语句
Switch Case 结构
与 else-if 嵌套相比, Switch-Case 更为简洁方便,但缺点则是其只能检测整形或枚举,且只能对比是否相同 一个标准的switch结构如下 switch(变量){
case 常量A:
变量与常量A相等时执行的语句
break; (可省略)
case 常量B:
变量与常量B相等时执行的语句
break; (可省略)
default: (可省略)
变量与以上内容都不相等时执行的语句
}
需要注意的是,当程序执行到case中,并通过了判断开始执行case块内语句的时候,程序会持续向下执行发,直到switch结构结束或遇到break
即
switch(a){
case 1:
puts("a is 1");
break;
case 2:
puts("a is 2");
case 3:
puts("a is 2 or 3");
break;
}
在此代码中,case 2判断成功时,程序会执行 puts("a is 2")和puts("a is 2 or 3"),在实际使用时务必记住这一点。
我们在之前的程序中,使用的是问号表达式,现在我们使用判断语句重写这部分代码
symbol = r2? '+' : '-';
//重写为
if(r2) //还记得前文所说,c语言中非零的值都为真
symbol = '+';
else
symbol = '-';
在重新写完所有的部分之后,我们的程序看起来这样
#include <cstdio>
#include <cstdlib>
#include <ctime>
int main(){
int r1, r2, r3, answer, input;
char symbol;
srand(time(NULL));
r1 = rand() % 10;
r2 = rand() % 2 ;
r3 = rand() % 10;
if(r2){
symbol = '+';
answer = r1 + r3;
}else{
symbol = '-';
answer = r1 - r3;
}
printf("%d%c%d=", r1, symbol, r3);
scanf("%d", &input);
if(input == answer)
puts("Right");
else
puts("Wrong");
getchar();
return 1;
}
循环
While 结构
一个标准的 while 结构如下
while(条件){
循环体
}
当符合条件时,执行循环体中的语句 其流程为
+
|
v
+---+---+
+->+ while +--+
| +---+---+ N|
| Y | |
| v |
| +---+---+ |
+--+ ..; | |
+-------+ |
|
+------+
|
v
与if中的条件表达式类似,while也以同样的方式执行这种判断
Do While 结构
一个标准的 do - while 结构如下
do{
循环体
}while(条件);
其流程为
+
|
v
+---+---+
+->+ do |
| +---+---+
| |
| v
| +---+---+
| | ..; |
| +---+---+
| |
| v
| +---+---+
+--+ While |
Y +---+---+
| N
v
你或许注意到了这二者的不同,他们的格式类似,但do-while不管条件是否被满足,一定会执行至少一次循环体
循环的编写技巧
下面列举一段程序,我会逐步展示一个循环流程是怎样运作的。
int main()
{
int a = 0;
while(a < 10){
a++;
if(a % 2 == 0) printf("%d ", a);
}
}
-> int a = 0;
[a = 0]
-> while(a < 10)
[a = 0, a<10 为真] 此时,a < 10 为真,所以执行循环体
-> a++;
[a = 1]
-> if(a % 2 == 0) printf("%d ", a);
[a = 1, a%2 = 1, a%2==0 为假] 此时,条件为假,不执行if判断内的语句
-> while(a < 10)
[a = 1, a<10 为真] 循环会继续执行
-> a++;
[a = 2]
-> if(a % 2 == 0) printf("%d ", a);
[a = 2, a%2 = 0, a%2==0 为真] 此时,条件成立,执行
-> printf("%d", a);
[a = 2] 屏幕上打印了 2 和一个空格
...
-> while(a < 10)
[a = 9, a<10 为真] 在执行了数次循环之后,a到了9,此时依然符合条件
-> a++;
[a = 10]
-> if(a % 2 == 0) printf("%d ", a);
[a = 10, a%2 = 0, a%2==0 为真]
-> printf("%d ", a) 在屏幕上输出10
-> while(a < 10)
[a = 10, a<10 为假] 这时,跳出循环
在整个过程中,我们需要从两个角度来考虑,分别是
循环值的更新 与 循环的边界
从之前的例子来思考,我们需要获取x ~ y的所有偶数
i = x;
while(i <= y){
i++;
if(i % 2 == 0) printf("%d ", i);
//此处 i % 2 == 0 可写为 !(i % 2) 但会极大降低程序可读性
}
对这段代码进行分析
首先对于循环值的更新,我们每次增加1,但是既然我们需要偶数,就可以每次增加2。同时,这段代码有另一个问题,对循环的边界进行分析的时候,起始i=x 的时候,if实际使用了 x+1作为循环内容,因此实际上这个例子并不是 x ~ y 而是 x+1 ~ y+1
针对这两点进行修改
i=x;
while(i <= y ){
printf("%d ", i);
i += 2;
}
很快,我们有了新的问题,由于我们每次增加2,而且去除了if,所以我们循环实际上需要的是从“x后第一个偶数”到“y前最后一个偶数”
再次进行修改
i = x + (x % 2);
while(i <= y){
printf("%d ", i);
i += 2;
}
与IF相同,while和后文中的for语句可以省略其花括号
while(i <= y)
printf("%d", (i += 2, i - 2));
但如此撰写会极大降低可读性
For 结构
在循环足够简单的情况下,我们就可以使用C语言提供的for结构来完成 一个标准的 for 结构如下
for (初始化; 条件; 递增){
循环体
}
当首次执行到for结构时,会仅此一次的执行初始化,然后进行条件判断,接下来执行循环体,当循环体代码结束以后,回到for语句执行递增部分。一个for语句与如下while语句类似
初始化
while(条件){
循环体
递增
}
前文中的例子,可以用如下方式撰写为for结构
for(int i = x + (x % 2); i <= y; i += 2)
printf("%d ", i);
如果你的编译器使用的是更为严格的C语言规范,
int i = x + (x % 2)
是无法通过编译的,你需要在循环外声明i,类似如下
int i;
for(i = x + (x % 2); i <= y; i += 2)
printf("%d ", i);
循环控制
- break 终止循环,程序会跳过整个结构执行下一条语句
for(;;){
if(){
+---------break;
| }
| }
|
+>something;
- continue 会跳过循环体的剩下部分,回到条件语句所在的地方并执行一次条件测试
+>while(){
| if(){
+---------continue;
}
something;
}
- 对于 do - while 结构会跳转到结尾的while处
- 对于 for 结构会跳转到递增部分
- goto 无条件跳转到 label 所在的语句,可以在程序的任何地方使用
something;
+-goto HERE;
|
| something;
| something;
|
+>HERE:something;
- 也可向上寻找
无限循环
在一些特殊的情况下,我们需要使用无限循环。
这些情况并非妥协,一般在使用无限循环的情况下,程序员会使用前文中循环控制语句来保证程序会有一个出口。
bool fContinue = true;
while(fContinue){
...
if(...) fContinue = false; //出口
}
while(1){
if(...) break; //出口
}
课题
回到课题之中,我们需要加入如下功能
加入乘除法、加入积分系统、同时我们会增加题目数量,最后再为我们的答题者记录分数以激励他学习。
对于除法,浮点类型的对比是十分不精确的,会有诸多问题发生,这并不利于我们的学习,因此我们增加一个限制是保留其整数部分。除此以外我们直到除数与被除数不能为零,我们需要额外增加一个判断来判断他们是否为零。
这个判断你可以使用if进行,也可以使用表达式中的短路技巧,但这或许会使你混乱。挑选你喜欢的方法吧
在测试这一部分的时候,或许你会发现你需要重复运行数次才能找到随机生成的除法,而且其随机数本身为零的情况又及其难以得知,这个问题我们将在后续解决。
现在我们要加入积分系统,我们规划十道题,并展示出用户的整体得分 我们需要在随机数生成之前开始循环,并且直到getchar (用以吸收输入的回车)为止。 最终,我们的代码是这样的
#include <cstdio>
#include <cstdlib>
#include <ctime>
int main(){
int r1, r2, r3, answer, input, score=0;
char symbol;
srand(time(NULL));
int i=10;
while(i--){
r1 = rand() % 10;
r2 = rand() % 4 ;
r3 = rand() % 10;
switch(r2){
case 0:
symbol = '+';
answer = r1 + r3;
break;
case 1:
symbol = '-';
answer = r1 - r3;
break;
case 2:
symbol = '*';
answer = r1 * r3;
break;
case 3:
symbol = '/';
r1 || (r1 = 1);
r3 || (r3 = 1);
answer = r1 / r3;
break;
}
printf("%d%c%d=", r1, symbol, r3);
scanf("%d", &input);
if(input == answer)
score += 1;
getchar();
}
printf("Your Mark Is: %d/10" ,score);
return 1;
}
其中,while(i--)所代表的是,从i-1开始循环至0,这是一个很常用的循环i次的方法,除此以外,你还可以使用for或常规的while编写,代码风格实际上没有对错可言。
作业!
试着修改循环条件和更新机制,让我们的用户只有“连续答对五道题”才可以结束这个循环!分享你的想法吧,
同时,想想如何设计程序流程,让用户可以在“连续的十道题中至少答对六道”才可以结束!下篇文章我会讲解数据结构的第一部分,"顺序表"的内容构的第一部分,"顺序表"的内容