07. C语言程序执行流程控制

news/2024/10/7 18:25:46


【有条件执行语句】

if esle 语句

if else 语句根据一个条件确定是否执行一段代码,执行条件是一个布尔值,布尔值为true则执行,为false则不执行,同时可以设置不符合条件时执行的语句。

if(执行条件)
{
    符合条件时执行的代码;
}
else
{
    不符合条件时执行的代码;
}

使用事项:
1.执行条件可以直接使用一个布尔数据,也可以是返回布尔值的运算式、函数,此时会首先执行一遍运算式或函数,之后使用其返回值当做执行条件。
2.若不需要在不符合条件时执行其它语句,可以省略else。
3.if和else内部代码可以有多个语句,若只有一个语句可以省略{}符号。

#include <stdio.h>
int main()
{
    int a, b;
    scanf("%d%d", &a, &b);    //输入变量a、b的值    if(a < b)
    {
        printf("a小于b\n");    //输出判断结果
    }
    else
    {
        printf("a不小于b\n");
    }
    
    return 0;
}

if else 语句对应汇编代码如下:

  401154:    mov    edx,DWORD PTR [rbp-0x4]    ;变量a写入edx
  401157:    mov    eax,DWORD PTR [rbp-0x8]    ;变量b写入eax
  40115a:    cmp    edx,eax                    ;比较a与b的关系
  40115c:    jge    40116a                     ;大于等于则跳转到else语句部分,小于则顺序执行if语句部分  40115e:    mov    edi,0x402009               ;if语句部分,将"a小于b\n"字符串地址写入edi传参
  401163:    call   401030                     ;跳转到输出函数
  401168:    jmp    401174                     ;if语句代码末尾,跳过else语句,执行之后的指令  40116a:    mov    edi,0x402012               ;else语句部分,将"a不小于b\n"字符串地址写入edi传参
  40116f:    call   401030                     ;跳转到输出函数  401174:    mov    eax,0x0                    ;return 0,设置main函数返回值
  401179:    leave                             ;还原栈空间
  40117a:    ret                               ;返回指令


某些情况下,编译器会进行以空间换时间的优化,此时if语句部分无需执行jmp指令跳过else语句部分,相当于转换为如下C代码:

#include <stdio.h>
int main()
{
    int a, b;
    scanf("%d%d", &a, &b);    if(a < b)
    {
        printf("a小于b\n");
        return 0;             //return用于终止函数
    }
    else
    {
        printf("a不小于b\n");
        return 0;
    }
}

嵌套 if else

if else 语句可以嵌套使用,从而进行多重条件判断,嵌套可以在if语句内、也可以在else语句内。

#include <stdio.h>
int main()
{
    int a,b;
    scanf("%d%d", &a, &b);
    
    if(a<b)
    {
        printf("a小于b\n");
    }
    else
    {
        if(a>b)
        {
            printf("a大于b\n");
        }
        else
        {
            printf("a等于b\n");
        }
    }
    
    return 0;
}

上述这种 if else 语句嵌套使用方式,可以使用如下简写形式:

#include <stdio.h>
int main()
{
    int a,b;
    scanf("%d%d", &a, &b);
    
    if(a<b)
    {
        printf("a小于b\n");
    }
    else if(a>b)
    {
        printf("a大于b\n");
    }
    else
    {
        printf("a等于b\n");
    }
    
    return 0;
}


switch case 语句

switch case 语句用于在多组语句中选择一组符合条件的执行,找到符合条件的语句后不再判断其它条件,功能类似多个if语句嵌套并在每个if内使用goto语句跳出嵌套,但是使用 switch case 会让代码更简洁,并且编译器会对 switch case 进行一些优化,执行速度更快。

switch (整数变量)
{
    case 整数常量:
    执行代码段;
    break;    case 整数常量:
    执行代码段;
    break;    case 整数常量:
    执行代码段;
    break;    default:
    执行代码段;
    break;
}

switch关键词之后设置一个变量,之后定义多个case语句,每个case语句设置一个常量,switch设置的变量会与case设置的常量进行比较,与哪个case常量相同就执行哪个case语句内代码段,若没有符合条件的case语句,则执行default语句内代码段,若不需要在不满足条件时执行另一段代码,default也可以省略。

注:case和default语句内代码段以break结尾,用于跳出switch语句,若没有设置break则会顺序执行此语句之后的代码,这将会导致逻辑混乱。

 

#include <stdio.h>
int main()
{
    unsigned int a;
    scanf("%u",&a);    //输入a的值
    
    switch(a)
    {
    case 0:
        printf("输入值为0\n");
        break;
    case 1:
        printf("输入值为1\n");
        break;
    case 2:
        printf("输入值为2\n");
        break;
    case 3:
        printf("输入值为3\n");
        break;
    default:
        printf("输入值大于3\n");
        break;
    }
    
    return 0;
}

以上switch语句对应的汇编代码如下:

401150:    mov    eax,DWORD PTR [rbp-0x4]    ;变量a写入eax  401153:    cmp    eax,0x1                    ;a与1比较
  401156:    je     401174                     ;a等于1则跳转到case 1  401158:    test   eax,eax                    ;a与0比较
  40115a:    je     401168                     ;a等于0则跳转到case 0  40115c:    cmp    eax,0x2                    ;a与2比较
  40115f:    je     401180                     ;a等于2则跳转到case 2  401161:    cmp    eax,0x3                    ;a与3比较
  401164:    je     40118c                     ;a等于3则跳转到case 3  401166:    jmp    401198                     ;若没有满足条件的case语句,跳转到default  401168:    mov    edi,0x402007               ;case 0 语句
  40116d:    call   401030                     ;终端输出函数
  401172:    jmp    4011a3                     ;跳转到switch case语句之后的代码,此处为跳转到return 0  401174:    mov    edi,0x402015               ;case 1 语句
  401179:    call   401030
  40117e:    jmp    4011a3  401180:    mov    edi,0x402023               ;case 2 语句
  401185:    call   401030
  40118a:    jmp    4011a3  40118c:    mov    edi,0x402031               ;case 3 语句
  401191:    call   401030
  401196:    jmp    4011a3  401198:    mov    edi,0x40203f               ;default 语句
  40119d:    call   401030  4011a3:    mov    eax,0x0                    ;return 0
  4011a8:    leave  
  4011a9:    ret


编译器使用了多个跳转指令,分别跳转到对应的case语句,当case语句过多时编译器会进行优化,将所有的case语句地址放在一个数组内(称为case地址表),之后使用switch设置的变量当做数组下标调用case地址表中的元素,从而减少跳转指令的使用量,具体细节这里不详细介绍,本文的重点是介绍C语言编程,而非C语言编译器。

条件运算式

条件运算式类似 if else 语句,但是条件运算式只能根据判断条件返回两个数据之一,此功能编译器可以进行一些优化,比使用 if else 语句执行速度更快。

条件 ? 数据1 : 数据2;

条件是一个布尔值,若为true则返回数据1,若为false则返回数据2。

#include <stdio.h>
int main()
{int a,b;printf("输入两个整数,判断最大值\n");scanf("%d%d", &a, &b);a = a>b ? a : b;    //若a>b则返回a,否则返回bprintf("最大值为%d\n", a);return 0;
}

条件运算式对应汇编代码:

  40116e:    mov    edx,DWORD PTR [rbp-0x8]    ;变量b写入edx
  401171:    mov    eax,DWORD PTR [rbp-0x4]    ;变量a写入eax
  401174:    cmp    edx,eax                    ;比较b与a
  401176:    cmovge eax,edx                    ;若b>=a则将b写入eax,否则eax存储a
  401179:    mov    DWORD PTR [rbp-0x4],eax    ;eax写入变量a

 


【循环语句】

循环语句用于循环执行一段代码,循环条件是一个布尔值,若为true则循环一遍,为false则退出循环。

while 循环

while循环首先判断循环条件,之后确定是否执行循环。

while(循环条件)
{
    循环代码段;
}
#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    int b = 0;
    
    /* 遍历数组a */
    while(b < 5)
    {
        printf("%d\n", a[b]);
        b++;
    }
    
    return 0;
}

上述循环语句对应汇编代码如下:

  40112a:    mov    DWORD PTR [rbp-0x20],0x1    ;数组a入栈存储
  401131:    mov    DWORD PTR [rbp-0x1c],0x2
  401138:    mov    DWORD PTR [rbp-0x18],0x3
  40113f:    mov    DWORD PTR [rbp-0x14],0x4
  401146:    mov    DWORD PTR [rbp-0x10],0x5  40114d:    mov    DWORD PTR [rbp-0x4],0x0     ;变量b赋值
  401154:    jmp    401177                      ;跳转到循环条件判断语句  401156:    mov    eax,DWORD PTR [rbp-0x4]     ;循环语句内部代码
  401159:    cdqe   
  40115b:    mov    edx,DWORD PTR [rbp+rax*4-0x20]
  40115f:    mov    eax,DWORD PTR [rbp-0x4]
  401162:    mov    esi,eax
  401164:    mov    edi,0x402004
  401169:    mov    eax,0x0
  40116e:    call   401030                      ;输出函数
  401173:    add    DWORD PTR [rbp-0x4],0x1     ;b++  401177:    cmp    DWORD PTR [rbp-0x4],0x4     ;循环条件判断语句
  40117b:    jle    401156                      ;b小于等于4(等同于小于5)则向前跳转,实现循环

编译器进行了一个优化,将循环条件判断代码放在了末尾,这样可以少使用一个跳转指令,降低CPU分支预测失败率,等于转换成了 do while 循环。

嵌套 while

循环语句可以嵌套定义,比如遍历二维数组时就需要使用嵌套循环语句。

#include <stdio.h>
int main()
{
    int a[3][5] =
    {
        {1,2,3,4,5},
        {11,12,13,14,15},
        {21,22,23,24,25}
    };
    int b = 0, c;
    
    while(b < 3)
    {
        c = 0;
        while(c < 5)
        {
            printf("%d,", a[b][c]);
            c++;
        }
        printf("\n");
        b++;
    }
    
    return 0;
}

有条件执行语句和循环语句也可以互相嵌套,在有条件执行语句内定义循环语句,或在循环语句内定义有条件执行语句。

无限循环

很多程序的工作就是无限循环,比如服务器端程序,不断的接收用户发送的网络数据,之后进行处理。

对于一些循环条件很复杂的有限循环,也可以使用无限循环代替,将循环条件放在循环语句内部,若满足条件则循环,否则执行break语句终止循环。

但是对于循环语句的某些终止条件由外界提供的情况,应该慎用无限循环,若出现意外情况可能会导致循环语句永远不会被终止,为了避免这种情况可以使用一个循环次数很大的有限循环代替无限循环,正常情况下不会执行如此多次的循环,即使循环语句最终没有使用break终止,也会因为循环次数达到上限而终止。

#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    int b = 0;
    
    while(1)    //循环条件固定为1
    {
        if(b > 4) break;    //b>4则执行break语句终止循环
        
        printf("%d\n", a[b]);
        b++;
    }
    
    return 0;
}

for 循环

for循环是while循环的另一种使用方式,for将循环条件变量的定义、判断、修改放在一起,方便查看代码,所有的for循环都可以使用while循环代替。

for(循环条件变量; 循环条件判断; 循环条件修改)
{
    循环代码段;
}

for循环使用说明:
1.循环条件变量,定义循环条件所用的变量,只会执行一次,相当于在for外部定义的一个变量,但是只能在for内部使用。
2.循环条件判断,循环条件变量参与的运算式,返回布尔值,若为true则执行一次循环,若为false则退出循环。
3.循环条件修改,修改循环条件变量的值。

#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    
    /* 变量i只定义一次,每次循环前首先判断i<5,每次循环后执行i++ */
    for(int i = 0; i < 5; i++)
    {
        printf("%d\n", a[i]);
    }
    
    return 0;
}

有些古老的编译器不支持上述用法,需要使用如下形式:

#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    
    int i;
    for(i = 0; i < 5; i++)
    {
        printf("%d\n", a[i]);
    }
    
    return 0;
}

for的无限循环形式:for(;;) { }

do while 循环

do while 循环语句将循环条件放在循环代码段之后,首先执行一遍循环代码,然后判断循环条件,若满足条件则继续循环,循环语句至少执行一次。

do
{
    循环代码段;
}while(循环条件);

 

#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    int b = 0;
    
    /* while定义在循环语句末尾,首先执行一次循环,之后判断循环条件 */
    do
    {
        printf("%d\n", a[b]);
        b++;
    }while(b < 5);
    
    return 0;
}


while 与 do while 的执行流程区别如下。

while循环语句执行顺序:
1.进入 while =》 满足条件 =》 不执行跳转,顺序执行一遍循环代码 =》 执行跳转,跳转到 while 起始地址
2.进入 while =》 不满足条件 =》 执行跳转,跳过 while

do while循环语句执行顺序:
1.进入 do while 直接执行循环代码 =》 满足条件 =》 执行跳转,跳转到 do while 起始地址
2.进入 do while 直接执行循环代码 =》 不满足条件 =》 不执行跳转,顺序执行 do while 之后的代码

在不满足循环条件时 while 会比 do while 多执行一个跳转指令,为了减少执行跳转指令,编译器会将 while 和 for 转换为 do while。
实际上之前介绍的 while 语句汇编代码已经转换为 do while,因为循环条件在编译期间即可确定成立,编译器判断循环语句至少会执行一次,若循环条件不能在编译期间确定是否为true,编译器也会将 while 转换为 do while,比如下面的代码。

#include <stdio.h>
int main()
{
    unsigned int a;
    scanf("%u", &a);    //程序执行期间输入a的值    /* 变量a递减,直到a为0 */
    while(a != 0)
    {
        printf("%d\n", a);
        a--;
    }    return 0;
}

编译器转换为如下代码:

#include <stdio.h>
int main()
{
    unsigned int a;
    scanf("%u", &a);
    
    if(a != 0)
    {
        do
        {
            printf("%d\n", a);
            a--;
        }while(a != 0);
    }
    
    return 0;
}

 


【跳转语句】

break 跳转

break语句有两个作用:
1.终止循环语句,执行循环语句之后的代码,对于嵌套使用的循环,break只终止所在的这层循环。
2.跳过switch case语句,执行switch case之后的代码。

猜数字游戏代码:

#include <stdio.h>
int main()
{
    int a=17, b=0;    //a为用户要猜测的数据,b存储用户输入的数据
    char x=0;         //清空标准输入文件使用
    
    printf("猜数字游戏,数据范围 0 - 100,请输入你认为的数据\n");
    
    /* 无限循环 */
    while(1)
    {
        scanf("%d", &b);    //输入b的值
        
        /* 清空标准输入文件,原因在C语言标准函数库一节中介绍 */
        while(x != 10)
        {
            x = getchar();
        }
        x = 0;
        
        if(b < a)
        {
            printf("太小了,请重新输入\n");
        }
        else if(b > a)
        {
            printf("太大了,请重新输入\n");
        }
        else
        {
            printf("猜对了,正确值是%d\n", a);
            
            break;    //用户输入正确数据后终止循环
        }
    }
    
    return 0;
}

continue 跳转

continue语句用于终止循环语句的本次循环,执行下一次循环,一般与if语句配合使用,在满足条件时放弃本次循环。

#include <stdio.h>
int main()
{
    int a[] = {1,2,3,4,5};
    
    for(int i = 0; i < 5; i++)
    {
        if(i == 3)
        {
            continue;    //i为3则放弃本次循环
        }
        
        printf("a[%d]=%d\n", i, a[i]);
    }
    
    return 0;
}

goto 跳转

goto语句用于无条件跳转到任意代码处执行,可以向前跳转也可以向后跳转,一般与if语句配合使用,实现有条件任意跳转,常用于复杂逻辑判断中,比如多个if语句和循环语句互相嵌套的情况,若没有goto语句则实现某些功能的代码会非常复杂。

#include <stdio.h>
int main()
{
    int a;
    scanf("%d", &a);      //输入a的值
    
    if(a == 0) goto x;    //x为自定义的地址名称,若a等于0,则跳过功能一代码,执行功能二代码,否则两段代码都执行
    
    //功能一代码 ......
    printf("功能一执行完毕\n");
    
    x:
    //功能二代码 ......
    printf("功能二执行完毕\n");
    
    return 0;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/28214.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

用蒙特卡罗方法求积分

实验任务 采用 Monte-Carlo 法计算函数 y=x2 在 0~10 之间的积分值 实验目的 熟悉 MPI_Reduce() 函数的用法 实验方法 该算法的思想是通过随机数把函数划分成小的矩形块,通过求矩形块的面积和来求积分值,我们生成 n 个 0~10 之间的随机数,求出该随机数所对应的函数值作为矩…

Kafka源码分析(四) - Server端-请求处理框架

Kafka源码分析,侧重于请求处理框架系列文章目录 https://zhuanlan.zhihu.com/p/367683572 一. 总体结构 先给一张概览图:服务端请求处理过程涉及到两个模块:kafka.network和kafka.server。 1.1 kafka.network 该包是kafka底层模块,提供了服务端NIO通信能力基础。 有4个核心…

一次通过dump文件分析OutOfMemoryError异常代码定位过程

OutOfMemoryError是Java程序中常见的异常,通常出现在内存不足时,导致程序无法运行。借助MAT内存分析工具分析可能的内存泄漏代码问题定位。OutOfMemoryError是Java程序中常见的异常,通常出现在内存不足时,导致程序无法运行。 当出现OutOfMemoryError异常时,可能的现象是这…

前端埋点数据采集(二)mock应用系统10万条前端埋点数据

前端埋点数据采集(二)mock应用系统10万条前端埋点数据 上一期我们分享了前端埋点数据采集(一)采集系统架构设计 我们说应用系统的数据,采集到大数据平台来,然后再到数仓。但是很多实际场景是应用系统、大数据平台、数仓平台各自并没有完成系统的搭建和开发。假设现在一个…

windows下安装Jenkins以及配置分布式agent节点

安装Jenkins: 1.Jenkins稳定版本的war包路径:https://get.jenkins.io/war-stable/ 2.jdk下载:https://www.oracle.com/java/technologies/downloads/ 3.启动Jenkins:命令行运行java -jar jenkins.war 至此可以通过浏览器127.0.0.1:8080,连接上本地Jenkins配置分布式agent节…

locust压测

目录locust1.依赖2. 实例2.1 压测方式2.2 locust服务端2.3 待压测接口服务3. 参考文档 locust 1.依赖 pip install locust2. 实例 2.1 压测方式 1. 压测方式 1.1 前台自编辑方式修改文件名为locustfile.py 并在控制台使用locust启动前台服务 用户自定义压测参数并开启压测1.2 …

如何快速找出文件夹里的全部带有符号纯符号的文件

参考此文章:如何快速找出文件夹里的全部带有中文&纯中文的文件 只需要根据自己的需求,把下面相关的设置调整好即可