AI智能
改变未来

【C语言从青铜到王者】第一篇·详解分支与循环

本篇前言
前文我们说到,编程的一个目的就是借助计算机帮助我们解决生活中的问题,而世界运行的基本时间逻辑,在向计算机描述时可以大致归纳成三种逻辑结构:顺序、选择、循环。
顺序就是从前往后,从上到下,我们的写好的程序就是按顺序从第一行起被一行一行的编译的
选择也叫分支,是同一时间点面对问题时,有不同的选择,我们选其一而行
循环是指程序进入了一段不断重复的路,可能会一直在此循环往复,也可以打破循环,继续前进。
在顺序执行的程序中,分支中可能有循环,循环中也可能有分支。分支和循环是让我们的代码变得多姿多彩的关键技能,今天就让我们来升级它们吧!

文章目录

  • 分支语句
  • if – else语句
  • 单if语句
  • 知识补充一
  • if – else双分支语句
  • if – else if – else多分支语句
    • 常见错误提醒一
  • 悬空else的匹配问题
    • 常见错误提醒二
  • switch语句
    • 初识switch语句
    • 必不可少的的break关键字
    • 知识补充二
  • 常用的default子句
    • 知识补充三
  • 分支语句的嵌套
    • if嵌套
    • switch嵌套
  • 循环语句
    • while循环
    • for循环
    • do-while循环
    • 循环语句中的break和continue
    • 循环语句的嵌套
  • goto语句
    • goto语句的使用方法
    • 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语句就像是一个保险,把深陷“多重梦境”中的程序一语点醒

    至此为止,第一篇的内容结束。博主才疏学浅,又讲解不周的地方希望大家积极指出,我们共同探讨,共同进步。

  • 赞(0) 打赏
    未经允许不得转载:爱站程序员基地 » 【C语言从青铜到王者】第一篇·详解分支与循环