首页 > 编程语言 >07. C语言程序执行流程控制

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

时间:2024-05-06 18:33:44浏览次数:24  
标签:语句 case 07 int C语言 while 循环 跳转 程序执行


【有条件执行语句】

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,否则返回b
	
	printf("最大值为%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;
}

 

标签:语句,case,07,int,C语言,while,循环,跳转,程序执行
From: https://www.cnblogs.com/alixyy/p/18175599

相关文章

  • C语言加强
    变量、指针和关键字两个口诀:变量变量,能变,就是能读能写,必定在内存(RAM)里指针指针,保存的是地址,32位处理器中的地址都是32位的,无论是什么类型的指针变量,都是4字节指针对于32位处理器里面,地址是32位的,所以指针的大小为4字节,sizeof(p)=4,sizeof(*p)=指针所指向的......
  • 高级C语言6
    结构:什么是结构:​ 是一种由程序员设计的复合数据类型,它由若干个其它类型的成员组成,用于统一描述事物的各项属性。​ 使用各类型的变量也可以描述事物的各项属性(如:通讯录项目),但使用麻烦且容易出错,没有使用结构方便,安全性高、统一性高,同时结构也是面向对象编程的基础。​ 基础C......
  • 高级C语言5
    输出缓冲区:​ 当我们使用标准库的输出系列函数打印数据到屏幕,数据并不会立即显示到屏幕上,而先存储到一块内存中,我们把这块内存称为输出缓冲区,等满足相关条件后,再从缓冲区中显示到屏幕,相关条件有:1、从输出状态切换到输入状态。2、缓冲区满了,1k=1024个字节,系统会把缓冲区中所有数......
  • 207. 课程表-python
    你这个学期必须选修numCourses门课程,记为0到numCourses-1。在选修某些课程之前需要一些先修课程。先修课程按数组prerequisites给出,其中prerequisites[i]=[ai,bi],表示如果要学习课程ai则必须先学习课程bi。例如,先修课程对[0,1]表示:想要学习课程0,你......
  • 高级C语言2
    计算机的内存长什么样子?1、计算机中的内存就像一叠非常厚的“便签”,一张便签就相当于一个字节的内存,一个字节有8个二进制位2、每一张“便签”都有自然排序的一个编号,计算机是根据便签的编号来访问、使用"便签"3、CPU会有若干个金手指,每根金手指能感知高低电平,高电平转换成1,低电......
  • 高级C语言1
    一、程序的内存分段:(进程映像)​ 当执行程序的运行命令后,操作系统会给程序分配它所需要的内存,并划分成以下内存段供程序使用:text代码段:​ C代码被翻译成二进制指令后存储在可执行文件中,当可执行文件被操作系统执行时,它会把里面的二进制指令(编译后的代码)加载到这个内存段,它里面......
  • 06. C语言指针
    【指针】C语言使用数据名调用数据,数据名相当于C语言的直接寻址,直接寻址只能调用固定数据,而指针是间接寻址,指针存储了另一个数据的地址,使用指针调用数据时首先取指针存储的内存地址,之后使用此地址调用数据,使用间接寻址有如下几点优势:1.统一数据的调用方式,因为指针是调用数据的中间......
  • 07.面向对象编程
    Java的核心思想就是OOP(面向对象编程)1.面向对象面向对象编程:Object-OrientedProgramming,OOP)本质:以类的方式组织代码,以对象的组织(封装)数据2.对象的创建2.1.创建与初始化对象创建对象:new使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化......
  • 标准C语言5
    进制转换:​ 现在的CPU只能识别高低两种电流,只能对二进制数据进行计算。​ 二进制数据虽然可以直接CPU计算识别,但不方便书写、记录,把二进制数据转换成八进制是为了方便记录在文档中。​随着CPU的不断发展位数不断增加,由早期的8位逐渐发展成现在的64位,因此八进制就不能满......
  • 标准C语言4
    一、函数什么是函数:function​函数就是一段具有某一项功能的代码集合,它是C语言中管理代码的最小单位,把具有某项功能的若干行代码封装在函数中方便管理代码且方便重复调用。函数的分类:标准库函数:​ C语言标准委员会为C语言以函数形式提供了一些基础功能,这些函数被封装在li......