教你理解分支语句和循环语句

一、学前简介

在我们日常的生活中,我们常用语气不同的语句来表示当前所感受的状态,如感叹句、疑问句、祈使句等等。

而在机器世界中,主要有:表达式语句、 函数调用语句、 控制语句、 复合语句、 空语句这五类语句来表示顺序结构、选择结构和循环结构。

而今天,主要介绍控制语句,用于选择结构和循环结构当中。

二、分支语句(选择语句)

1.if语句

//语法结构:①(无分支)
if (表达式)    //表达式为真,执行语句,表达式为假,不执行语句
    语句;



//②双分支
if (表达式) 
    语句1;    //表达式为真,则执行语句1,表达式为假,则执行语句2
else 
    语句2;



//③多分支
if(表达式1)
    语句1;        
else if(表达式2)       //表达式1成立,则执行语句1    
    语句2;             //表达式1不成立,表达式2成立,则执行语句2
else                   //表达式1和2都不成立,则执行语句3
    语句3;

特点:

①表达式为真的数学表示是非零,为假的数学表示是零(0为假,非0为真)

②对于多分支语句来说(控制语句>3)来说,中间用的是else if+(表达式)

③表达式的识别中age大于18小于30的写法必须按照机器语言来写-上图(而不是数学语言-下图)

 为什么会出现上面的这种情况?

首先要知道,数学语言的这种写法是错误的。

当编译器从左到右进行分析的时候,首先先会比较18<=age是否成立,由于18<=10不成立,为假返回0,因此生成的表达式变成了0<=30,为真,便开始执行语句1。

④if语句默认后面只能跟一条语句(条件成立才执行),如果要想跟多条语句,必须用  “{}”将所有语句包含。

 ⑤不论是双分支还是多分支语句,本质上是一条语句-即是从所有的语句中选择一条语句来执行。

 编程好习惯:个人建议无论if是否多分支,选择语句是否多句,都要加上{} 包含语句

1.1有关于if语句中的悬空else

曾在学习中遇见这样的一个代码:

不妨在往下翻阅的花之前上一分钟去思考一下这个代码段输出的最终结果会是什么?

#include<stdio.h>

int main ()
{
    int a = 0;
    int b = 2;
    if (1==a)
        if (2==b)
            printf("hello world\n");
    else 
            printf("world hello\n");
    
    return 0;
}

可能有的同学会疑问,为什么会产生这样的一个结果?事实上,我对本地环境下的代码快其实是进行了一个小小的改动。

因此能够获得两个结论:

①无论else在编译器打出的对应格式是怎么样的,else总是与其最近的if相匹配。

②告诉我们一段良好的代码一定有其良好的代码风格,用来增强其可读性。(如下)

//代码1-不良的风格
if (condition){
    return x;
}
return y;

//代码2-可读性较强的风格
if (condition)
{
    return x;
}
else 
{
    return y;
}

//事实上,代码1和代码2所表达的意思是一样的,在条件成立下返回x,不成立便返回y。
//但代码1容易让新手产生一种我既可以返回x,又会返回y的错觉。
//此外,{这一左半部分紧跟表达式的后面,新手容易多加或者遗漏。因此不建议写代码1这样的风格。

2.switch语句

虽然if语句有提供多分支语句,但是代码的本质是将复杂的东西简介化,else if语句太多会加剧时间成本消耗。

例如:输入1,便输出星期1,输入2,便输出星期2....以此类推到7,难道要用上5个else if语句?

为了避免这种情况,我们引入了switch语句。

switch(整形表达式)
{
    语句项;            //注意,{}外面没有;
}

//语句项:
case(整形常量表达式): //注意冒号
     语句;

注意:有关于switch中的整形表达式其实是一个广义上的概念

实际上与int类型用同样存储方式放入内存当中的类型都算是整形表达式-最容易忽略的是枚举类型和char类型(字符转换成ASCII值存于内存)

如下是一个应用switch语句的例子:

#include<stdio.h>

int main()
{
    int day = 0;

    scanf("%d", &day);
    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;
}

缺点:

①由于case后面必须是常量,因此,并不是所有的多分支都能用switch语句来解决。

②常量输入范围过大,同样也不适合用switch语句。

2.1switch中的break

当我们兴高采烈地完成输出星期几的工作后,却在执行的时候发现了如下情况:

为什么会产生这样的结果?

实际上,当你输入整形表达式的时候,会去匹配对应语句项中常量表达式的值,如同main()函数一样,提供一个执行的入口。然而,case的功能也仅仅是提供一个入口,没有出口,也不会阻止程序往下执行,直至return 0这个出口。

而break,则是每个语句项之中添加一个出口。

 那么,是否每个语句项(case语句-没有特定的语句顺序)都要+break?-不需要,按需要写代码,如下。

#include<stdio.h>

int main()
{
    int day = 0;

    scanf("%d", &day);
    switch (day)
    {
    case 1:    
    case 2:
    case 3:
    case 4:
    case 5:
        printf("工作日\n");
        break;
    case 6:
    case 7:
        printf("休息日\n");
        break;
    }

    return 0;
}

编程好习惯:建议最后一条case语句后+break-防止在添加新case语句的时候连续执行。

2.2default字句(每一个switch语句中有且只有一条)

或许,用户突发奇想,在switch中的整形常量表达式中输入了一个特别的式子,没有一个语句项能与之对应。那么没有入口的程序改如何执行呢?或者说如何提醒用户,其输入的值是有问题的呢?这时候,我们就要利用default子句。

#include<stdio.h>

int main()
{
    int day = 0;

    scanf("%d", &day);
    switch (day)
    {
    case 1:    
    case 2:
    case 3:
    case 4:
    case 5:
        printf("工作日\n");
        break;
    case 6:
    case 7:
        printf("休息日\n");
        break;
    default:
        printf("输入错误\n");
        break;
    }

    return 0;
}

 有关于default语句的特点:

其实default语句(case也可类比)存放在语句项中并不一定要放在最末尾的位置,它的顺序是任意的。然而我们一般习惯将其放在末尾,主要有以下两个原因。

其一:符合我们逻辑上的认知,先将正确的东西处理完毕,再去处理错误需要改正的东西。

其二:防止default子句语句中因忘记+break导致没有出口所执行出的程序与预期不符。

三.循环语句

1.while循环(用法类似与上方if语句-表达式为真是执行循环的条件)

//while 语法结构    //①表达式为假,不进入循环
while (表达式)
    循环语句;    //②表达式为真,进入循环执行循环语句,执行完毕后,再次判断其表达式,循环往复。直至表达式为假,跳出循环。

对应流程图:

从上图,我们了解到了循环体中还有两个指令,下面便介绍一下这两个指令对应的功能。

1.1break命令(在某种条件下让循环提前终止-永久终止(跳出)本层循环)

 跳出本层循环是什么意思?

while (expr1)
{
    while(expr2)
    {
        if(condition)
          {  
              break;
          }
        语句1;
        语句2;
        ……;
    }
    语句n;       //condition为真时,break只能跳出expr2表达式判断的循环,跳转到语句n开始执行。
}                //一次break只能跳出一次循环。

1.2continue命令(不执行continue后面的语句-直接跳到下一次的循环表达式判断)

 2.for循环

int main()
{
    int i = 1;//初始化

    while (i <= 10) //判断
    {
        printf("&d",i);

        i++ //调整
    }

    return 0;
}

如上代码,我们不难发现,一般情况下,一个循环(不含死循环)要正常运行它的功能一般需要初始化、判断以及调整三部分。

如果每次我们进行循环的时候,都要去对应的代码块去找初始化、判断以及调整三部分将其修改成所需(如果代码风格好还好说,代码风格不好简直令人头大)。那么有没有一种循环能够直接在一个地方完成这三部分呢?-for循环因此诞生。

2.1for循环对应语法:(和if同存在多语句用{}的用法)

 其中执行的顺序是-表达式1-表达式2-循环语句-表达式3-表达式2-循环语句-表达式3…………

注意:除非从for循环上面的语句再次for循环,否则for循环的表达式1初始化只进行一次)

2.2break语句和continue语句

for循环中的break语句的用途与while循环类似,然而continue则和while循环有些许差异

continue在for循环的差异(如下流程图)

 continue后不是跳到expr2的判断,而是跳到expr3的调整,执行后再进行判断是否循环

 2.3for循环语句的小建议(只是建议,并不绝对)

①不可在for 循环体内修改循环变量,防止 for 循环失去控制。-既然已经把调整放到表达式3,便不要在循环内部放调整

int main()
{
    int i = 0;
    for (i=1; i<=10 ;i++)
    {
         if(5==i)
             continue;
         i=2;            //内部修改循环变量导致死循环
         printf("%d ",i);
    }

    return 0;
}

②建议for语句的循环控制变量的取值采用“前闭后开区间”写法。

int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10};
    int i = 0;

    for (i = 0; i<10; i++)            //左边右开的形式表示代码循环10次,可读性高(相比于<=9)
    {
        printf("%d ",arr[i});
    }

    return 0;
}

2.4有关于for循环的变种

①省略了初始化部分,判断部分,调整部分

int main()
{
    for(;;)     //三个部分都可省略,但不建议,容易导致错误,且判断部分省略后,判断恒为真
    {
        printf("hehe\n"); 
    }    
    
    return 0;
}

②省略初始化部分

//代码1 
int i = 0;
 int j = 0;
    
for(i=0; i<10; i++)   //打印100个hehe
{
    for(j=0; j<10; j++)
    {
        printf("hehe\n");
    }
}
    
//代码2
int i = 0;
int j = 0;

for(; i<10; i++)
{
    for(; j<10; j++) //由于进入这个循环时,没有初始化,j没有重新赋值,因此只打印10次hehe
    {
         printf("hehe\n");
    }
}

③使用多个变量控制循环(少见)

int x, y;
    
for (x = 0, y = 0; x<2 && y<5; ++x, y++)
{
    printf("hehe\n");
}
return 0;

3.do...while循环(上来就执行一次,做完再判断要不要继续干这个事情-所以循环体至少执行一次)

3.1对应语法(和if同存在多语句用{}的用法)

do 
    循环语句;
while(表达式);

3.2对于do..while循环,其continue和break的用法和while循环相同

四、特殊的分支语句goto语句

4.1goto语句介绍+简单用法

C 语言中提供了可以随意滥用的 goto 语句和标记跳转的标号。
从理论上 goto 语句是没有必要的,实践中没有 goto 语句也可以很容易的写出代码。
但是某些场合下 goto 语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。
例如:一次跳出两层或多层循环。
多层循环这种情况使用 break 是达不到目的的。它只能从最内层循环退出到上一层的循环。

 

goto语句由于其跳转的属性,容易使得代码的可读性变低。事实上,用到goto的地方其实大部分都是可以用循环语句来解决的,因此goto语句也渐渐淡出了视野。

4.2goto语句的例子-关机程序

#include <stdio.h>
int main()
{
    char input[10] = {0};
    system("shutdown -s -t 60");
again:
    printf("电脑将在1分钟内关机,如果输入:我是猪,就取消关机!\n请输入:>");
    scanf("%s", input);
    if(0 == strcmp(input, "我是猪"))
   {
        system("shutdown -a");
   }
    else
   {
        goto again;
   }
    return 0; 
}

五、结束语

人生没有一帆风顺,也会出现选择和面临循环往复执行任务的事情。其中学习体现的最为明显。

将学习变成循环,在循环的途中做出好好学习,努力学习等等的选择,等到有一天遇到break出口之后,你会发现自己获取了成功,收获了幸福。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值