本篇前言
前文我们说到,编程的一个目的就是借助计算机帮助我们解决生活中的问题,而世界运行的基本时间逻辑,在向计算机描述时可以大致归纳成三种逻辑结构:顺序、选择、循环。
顺序就是从前往后,从上到下,我们的写好的程序就是按顺序从第一行起被一行一行的编译的
选择也叫分支,是同一时间点面对问题时,有不同的选择,我们选其一而行
循环是指程序进入了一段不断重复的路,可能会一直在此循环往复,也可以打破循环,继续前进。
在顺序执行的程序中,分支中可能有循环,循环中也可能有分支。分支和循环是让我们的代码变得多姿多彩的关键技能,今天就让我们来升级它们吧!
文章目录
- 分支语句
- if – else语句
- 单if语句
- 知识补充一
- 常见错误提醒一
- 常见错误提醒二
- 初识switch语句
- 知识补充三
- if嵌套
- while循环
- goto语句的使用方法
分支语句
if – else语句
if-else语句就是由if、else(、else if)组成的语句块。它是分支语句的一种,也是我们写出选择结构必须掌握的语句
我在这里不厌其烦的把if-else语句分成了三种:单if语句、if-else双分支语句、if-else if-else多分支语句
单if语句
单if语句就是没有else只有if的语句。
if(条件表达式)
{
执行的语句;
}
if的英文意思是“如果”,我们可以按照语义理解成“如果我满足条件就执行语句”。单if语句完成的就是这么一个功能。那如果不满足条件呢?不满足条件,就不执行语句。这里可以看出if-else不是一定得配套使用,只要有if就可以做出最基本的判断,这个特征在if-else if-else语句中同样适用,所以以后见到只有if或只有if-else if的语句就不要觉得奇怪了。
给出样例代码:
#include<stdio.h>int main(){int age = 20;if (age > 18)printf("可以考驾照\\n");return 0;}
代码分析:由于age=20,20>18,if后()里的条件判断为真,所以执行语句printf(“可以考驾照\\n”);
知识补充一
往后讲之前我们先补充两个基础知识:
给出样例代码:
#include<stdio.h>int main(){int a = 0;scanf("%d", &a);if (a < 60){printf("挂科\\n");}else{printf("稳过\\n");}return 0;}
1.C语言的真和假与if语句判断的条件
C语言中的“真”和“假”可以简单的理解成“非零”和“零”。选择语句其实就是通过判断条件的真假来“做出选择”,这个条件就是写在if后面的()里的表达式。上面的程序中,这个条件就是“a<60”,前面我们说过“<”的意思是“判断是否小于”,结果为真就输出1,结果为假就输出0。所以“a<60”的意思就是判断a是否小于60,我们通过scanf键入的a的值是60,所以判断为假,表达式的结果是0,为假,执行else语句,屏幕打印出“稳过”。
(博主希望大家在都能稳过( ̄▽ ̄))
2.if后的执行语句加不加大括号的问题
通过下面的程序来看不加大括号的情况。不加大括号时,if条件判断为真时会默认只执行其后最近的一条语句
#include<stdio.h>int main(){int age = 14;if (age < 18)printf("未成年\\n");return 0;}
通过下面的程序看一下加大括号的情况。加大括号时,if条件判断为真时会执行大括号内所有语句
#include<stdio.h>int main(){int age = 14;if (age < 18){printf("未成年\\n");printf("不能谈恋爱\\n");}return 0;}
再看下面这段程序,我们的age明明已经到18了却仍然不能谈恋爱,这是为什么呢?原因就是“不加大括号时if仅与其后最近的一条语句配对”,由于age是18,条件判断为假,所以不执行printf(“未成年\\n”);然后不论条件真假,程序都会按照顺序执行printf(“不能谈恋爱\\n”);
#include<stdio.h>int main(){int age = 18;if (age < 18)printf("未成年\\n");printf("不能谈恋爱\\n");return 0;}
知识总结:
1.C语言中if后的条件判断:非零为真,零为假
2.不加大括号时if仅与其后最近的一条语句配对
if – else双分支语句
双分支语句其实就是判断题,非if即else
给出语法:
if(条件表达式)
{
执行的语句1;
}
else
{
执行的语句2;
}
条件表达式为真,执行if后语句,条件表达式为假,执行else后语句
案例:
#include<stdio.h>int main(){int age = 0;scanf("%d", &age);if (age < 18)printf("未成年\\n");elseprintf("成年\\n");return 0;}
if – else if – else多分支语句
多分支语句就是将判断题变成了多选题
给出语法:
if(条件表达式1)
{
执行的语句1;
}
else if(条件表达式2)
{
执行的语句2;
}
…
else if(条件表达式n)
{
执行的语句n;
}
else
{
执行的语句n+1;
}
条件表达式1为真则执行语句1,条件表达式2为真则执行语句2,以此类推,条件表达式n为真,执行语句n; 如果,以上的n个条件都不满足,执行else下的语句n+1。
案例:
int main(){int score = 80;if (score >= 0 && score < 60)printf("挂科\\n");else if (score >= 60 && score < 70)printf("中等\\n");else if (score >= 70 && score <= 90)printf("良好\\n");else if (score >= 90 && score <= 100)printf("优秀\\n");elseprintf("成绩出错\\n");}
小问题:每个条件表达式的分数段的下限一定需要写出来吗?答案是不需要,因为当前一个条件判断不过时,已经自动的确认了后一个条件判断的下限。
常见错误提醒一
初学者可能会把这条语句
if (score >= 0 && score < 60)
写成
if (0 <= score < 60)
如果我们这么写,我们来测试一下程序的结果是什么(我们假设成绩是80)
明明是80分怎么会挂科呢?是改卷老师心情不好吗?并不是,其实这条语句
if (0 <= score < 60)
的逻辑是:先执行“0 <= score”,score为80,所以表达式“0 <= score”为真,表达式的结果就是1,然后“1 < 60”这个表达式也是真,所以if后()内的表达式值为1,直接打印了“挂科”。由此启示我们,不要想当然的把数学中的表达式写在程序中,C语言有自己的语法,我们要严格的遵守它才能让它为我们所用。
悬空else的匹配问题
看这段代码,先自行计算结果
int main(){int a = 10;int b = 20;if (a == 20)if (b == 20)printf("%d", b);elseprintf("%d", a);return 0;}
结果是什么呢?
结果是什么都不打印。原因是else默认与离自己最近的未匹配的if匹配。上面的程序书写风格不好,在我们的编程软件(Visual Studio 2019)上这段代码实际上会被自动的排版成下面这种形式。
int main(){int a = 10;int b = 20;if (a == 20)if (b == 20)printf("%d", b);elseprintf("%d", a);return 0;}
小伙伴们一定要学习养成好的代码风格!当然,我们也应该具备识别书写不规范代码的能力
常见错误提醒二
初学者可能会把这句
if (a == 20)
写成
if (a = 20)
那么这个表达式就不再是一个判断的表达式,而是一个赋值的表达式,表达式的结果就是a的值,为20,非零,为真,程序会直接执行if的语句,而且更糟糕的是,我们如果以后在很长的程序中不小心犯了这样的错误,编译器还不会报错,但是程序的结果肯定是不对的,那可就真是“死都不知道怎么死的”了。
为了杜绝这种错误,我们学习一个小技巧
小技巧:
上面的语句可以写成
if (20 == a)
这样的话如果我们哪天丢掉了一个=,写成了
if (20 = a)
程序就会直接报错
switch语句
初识switch语句
switch语句是一种适用于处理多分支的语句
给出语法:
switch (整形表达式)
{
case 整形常量表达式1:
语句1;
case 整形常量表达式2:
语句2;
}
switch后面的()内是整形表达式,就是说这个表达式结果的数据类型必须是整形(实际上字符型char也可以,以后遇到再细说) case后面的表达式是整形常量表达式,就是说这个表达式不仅结果必须是整形,而且结果必须是常量
这个语法是什么意思呢?我们这样理解:switch的英文意思是开关,那么switch后面整形表达式的值代表的就是这个开关在哪里被打开,也就是说这个整形表达式的值决定了整个switch程序块的入口
比如下面的代码:
int main(){int day = 3;switch (day){case 1:printf("工作\\n");case 2:printf("工作\\n");case 3:printf("工作\\n");case 4:printf("工作\\n");case 5:printf("工作\\n");case 6:printf("休息\\n");case 7:printf("休息\\n");}return 0;}
从上面的程序中就可以看出,程序是从上往下的,从这个入口(day = 3,也就是case 3)开始,一行一行的执行。但是光是这样,switch是做不到分支的功能的,所以我们还需要一个出口
必不可少的的break关键字
break充当的是switch程序块出口的角色。break是一个关键字,作用是让程序读到它时跳出自己当前所在的子程序。这样,程序块就被分划成了不同的部分,我们才能实现分支的功能
int main(){int day = 6;switch (day){case 1:printf("工作\\n");break;case 2:printf("工作\\n");break;case 3:printf("工作\\n");break;case 4:printf("工作\\n");break;case 5:printf("工作\\n");break;case 6:printf("休息\\n");break;case 7:printf("休息\\n");break;}return 0;}
比如这个程序就从case 6入口进入,然后读了一句printf(“休息\\n”),再读到break出口出选择程序块
知识补充二
swtich语句块的最后一项不管加不加break都可以跳出程序,但是编程的好习惯是加上,这样便于以后别人或我们修改程序增添case
常用的default子句
default子句和if-else中的else作用类似,都是将前面所有情况的补集作为自己的触发条件,也就是说前面的所有情况的条件判断都为假就执行它,而且它和else一样,并不是必须得存在。由于是所有情况的补集,所以default和else都至多只能出现一次
给出案例:
int main(){int day = 8;switch (day){case 1:printf("工作\\n");break;case 2:printf("工作\\n");break;case 3:printf("工作\\n");break;case 4:printf("工作\\n");break;case 5:printf("工作\\n");break;case 6:printf("休息\\n");break;case 7:printf("休息\\n");break;default:printf("输入错误\\n");break;}return 0;}
由于8不在任何一个case里,所以它在前面所有情况的子集:default里,所以执行default子句内容
知识补充三
else和default有一个很大的区别就是它们在程序中的位置问题:
default可以在任意位置,而else只能在最后
比如上面的程序写成
int main(){int day = 8;switch (day){case 1:printf("工作\\n");break;case 2:printf("工作\\n");break;case 3:printf("工作\\n");break;case 4:printf("工作\\n");break;case 5:printf("工作\\n");break;default:printf("输入错误\\n");break;case 6:printf("休息\\n");break;case 7:printf("休息\\n");break;}return 0;}
一点问题没有,程序照常执行
但是如果else放在了else if的前面,程序就会报错
这是因为switch实际上是把所有的情况看成了一块大饼切成不同的块块儿,块块儿的名字无所谓,反正都是块块儿。而if-else语句是有逻辑结构的,如果else放在了前面就和if直接配对形成程序块,后面的else if就无家可归了。
分支语句的嵌套
分支语句也就是if语句和switch语句,都是可以嵌套的。所谓分支嵌套就是在分支中再加入分支,这样我们就可以描述相对比较复杂的情况了
if嵌套
int main(){int a = 1;int b = 0;if (a == 1){printf("考入名校\\n");printf("你是否要努力学习?(1/0):");scanf("%d", &b);if (b == 1)printf("成为大神\\n");elseprintf("放飞自我,毕业即失业\\n");}return 0;}
switch嵌套
int main(){int day = 8;switch (day){case 1:printf("工作\\n");break;case 2:printf("工作\\n");break;case 3:printf("工作\\n");int a = 1;switch (a){case 1:printf("划水\\n");break;case2:printf("努力工作\\n");break;}break;case 4:printf("工作\\n");break;case 5:printf("工作\\n");break;default:printf("输入错误\\n");break;case 6:printf("休息\\n");break;case 7:printf("休息\\n");break;}return 0;}
循环语句
循环语句就是一些语句重复执行多次。C语言中的循环结构有三种常见的语句,分别是while、for、do-while循环,它们大同小异,都具有初始化部分、条件判断部分、调整部分,我们抓住这三个点来学习他们吧。
while循环
给出语法:
while(表达式)
{
循环语句;
}
其他语句;
表达式的值为真(非零),就重复执行循环语句,直到表达式的值为假(零),跳出循环,执行其他语句
案例“打印1-10”:
#include<stdio.h>int main(){int i = 1;while(i <= 10){printf("%d ", i);i++;}return 0;}
初始化部分:
int i = 1;
初始化部分的作用就是初始化一个循环变量
条件判断部分:
(i <= 10)
条件判断部分就是我们初始化的这个循环变量的作用所在。循环变量的值的改变会导致条件判断表达式的值改变,从而决定循环是否继续。条件判断为真,就继续循环,条件判断为假,就跳出循环
调整部分:
i++;
调整部分就是让我们的循环变量的值发生改变的语句,这样我们的条件判断表达式的值才有可能改变,不会进入死循环(停不下来的循环就是死循环)
(这里的i++是i=i+1的意思,前文讲过,这里仍然提示一下)
循环体
printf("%d ", i);
循环体就是每次循环都会执行的循环语句,是我们循环语句的主体内容
for循环
清楚了while循环,后面两个就可以类比学习了
给出语法:
for(初始化部分;条件判断部分;调整部分)
{
循环语句;
}
对比于while比较分散的三部分,for就集中的多,直接将三个部分写在了for后面的括号中,清晰明了
同样给出“打印1-10”的案例方便同学们类比学习:
#include<stdio.h>int main(){int i = 0;for (i = 1; i <= 10; i++){printf("%d ", i);}return 0;}
for循环还有几个注意点:
- 1.一般不建议在for循环体中修改循环变量,可能会导致程序错乱
- 2.for循环的三个部分可以都为空,直接用来表示死循环
for (;;){printf("阿巴");}
(光标一直在闪烁,说明程序一直在运行)
- for循环可以同时使用多个循环变量
#include<stdio.h>int main(){int x, y;for (x = 0, y = 0; x < 2 && y < 5; x++, y += 2){printf("%d%d", x, y);}return 0;}
do-while循环
给出语法:
do
{
循环语句
} while(条件判断表达式);
程序逻辑:先执行一次循环语句,再进行条件判断是否继续循环。与for和while循环的区别是至少可以循环一次。
给出类比案例:
#include<stdio.h>int main(){int i = 1;do{printf("%d ", i);i++;} while (i <= 10);return 0;}
do-while循环使用场景有限。总体的使用频率来说:for循环 > while循环 > do while循环
循环语句中的break和continue
- break
当程序读到break关键字的时候,直接跳出循环
举例:
#include<stdio.h>int main(){int i = 0;for (i = 1; i <= 10; i++){if (i == 5)break;printf("%d ", i);}return 0;}
当i累加到5的时候,执行break,直接跳出循环,程序结束
- continue
当程序读到continue关键字的时候,跳过本次循环中continue以后的循环语句
for循环中continue举例:
#include<stdio.h>int main(){int i = 0;for (i = 1; i <= 10; i++){if (i == 5)continue;printf("%d ", i);}return 0;}
当 i 累加到5的时候,执行continue,跳过本次循环剩下的语句printf("%d ", i),直接跳转到调整语句i++,然后进入下一次的条件判断语句,开始下一轮循环
while循环中continue举例:
#include<stdio.h>int main(){int i = 1;while(i <= 10){if (i == 5)continue;printf("%d ", i);i++;}return 0;}
程序结果是1 2 3 4 然后死循环(光标一直在闪烁说明程序一直未结束),这是因为i累加到5时,执行continue语句,跳过本次循环剩下的语句printf("%d “, i)和i++,直接跳转到条件判断 i <= 10,判断i=5 , 5<=10,然后又跳过本次循环剩下的语句printf(”%d ", i)和i++,直接跳转到条件判断 i <= 10,判断i=5 , 5<=10…
总结:
for循环中,由于调整部分不在循环体中,所以不会因为continue的影响陷入死循环;while循环中,调整部分在循环体中,极有可能因为continue陷入死循环
循环语句的嵌套
循环语句的嵌套就是在循环体中再加入另一个循环,从而会让我们的程序可以描述更多的循环情况,也提升了程序的空间复杂度
给出for嵌套案例:
#include<stdio.h>int main(){int i = 0;int j = 0;for (i = 1; i <= 10; i++){for (j = 1; j <= 10; j++){printf("%d ", 10 * (i - 1) + j);}printf("\\n");}return 0;}
打印九九乘法口诀表:
#include<stdio.h>int main(){int arr[] = { 1,2,3,4,5,6,7,8,9 };int i = 0;int j = 0;for (i = 0; i < 9; i++){for (j = 0; j <= i; j++){printf("%d × %d = %d ", arr[i], arr[j], arr[i] * arr[j]);}printf("\\n");}return 0;}
goto语句
goto语句的使用方法
goto语句相当于一个传送门,可以直接暴力的改变代码执行的逻辑,它就像一把双刃剑,用得好确实有奇效,但滥用可能会自废武功
给出语法:
goto 标记名称;
标记名称:其他语句;
用一个简单的例子认识一下使用方法
#include<stdio.h>int main(){int age = 0;again:scanf("%d", &age);if (age < 18 && age >= 1)printf("未成年\\n");else if (age >= 18 && age <= 121)printf("成年\\n");else{printf("输入错误,请重新输入\\n");goto again;}return 0;}
我们输入200的时候,执行else里的语句,先执行printf(“输入错误,请重新输入\\n”)然后执行goto again,again在程序最开始,所以我们可以继续进行选择判断。
goto语句的使用场景
goto是一把双刃剑,我们需要学会使用它的场景
使用goto的一个非常合适的场景就是用于跳出多重循环
看下面这个案例:
#include<stdio.h>int main(){int x, y, z;for (x = 0; x <= 100 / 5; x++)for (y = 0; y <= 100 / 3; y++)for (z = 0; z <= 100 * 3; z++){if ((x * 5 + y * 3 + z / 3 == 100) && (z % 3 == 0) && (x + y + z == 100)){printf("x=%d y=%d z=%d\\n", x, y, z);}elsegoto error;}error:return 0;}
这里的goto语句就像是一个保险,把深陷“多重梦境”中的程序一语点醒
至此为止,第一篇的内容结束。博主才疏学浅,又讲解不周的地方希望大家积极指出,我们共同探讨,共同进步。