C语言——分支与循环语句

一、什么是语句?

        C语言的语句可以分为以下五类:

        1、表达式语句

        2、函数调用语句

        3、复合语句

        4、空语句

        5、控制语句

C语句的分类

int a = b + c;//表达式语句

void swap(int a,int b);//函数声明
swap(a,b);//函数调用语句

{
    ......
}
//复合语句

;//空语句

         而控制语句主要用于控制程序的执行流程以实现程序的各种结构方式,它们由特定的语句定义符组成。在C语言中共有9种控制语句,可以分为以下三类:

        1、条件判断/分支语句:if语句、switch语句;

        2、循环执行语句:do while语句、while语句、for语句;

        3、转向语句:break语句、goto语句、continue语句、return语句。

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

        fda166c27b614e65952d57ce36b0a83b.png

 这就是选择.jpg

1、if语句

(1)语法结构

结构1:
if(表达式)
    语句;

结构2:
if(表达式)
    语句1;
else
    语句2;

结构3:
if(表达式)
    语句1;
else if(表达式)
    语句2;
else
    语句3;

        值得注意的是,如果条件成立需要执行多条语句时,应该使用代码块

//代码块if语句使用实例
#include <stdio.h>
int main()
{
    if(表达式)
    {
        语句列表1;
    }
    else
    {
        语句列表2;
    }
    return 0;
}

//这里{   }就是代码块

(2)执行条件

        如果表达式结果为真,则语句执行(Tips:C语言中,0为假,非0为真)

        **下面有几个代码,读者可以尝试运行,查看结果。

//代码1
#include <stdio.h>
int main()
{
    int age = 0;
    scanf("%d",&age);
    if(age<18)
        printf("未成年\n");
    return 0;
}
//代码2
#include <stdio.h>
int main()
{
    int age = 0;
    scanf("%d",&age);
    if(age<18)
        printf("未成年\n");
    else
        printf("成年\n");
    return 0;
}
//代码3
#include <stdio.h>
int main()
{
    int age = 0;
    scanf("%d",&age);
    if(age<18)
        printf("少年\n");
    else if(age>=18 && age<30)
        printf("青年\n");
    else if(age>=30 && age<50)
        printf("中年\n");
    else if(age>=50 && age<80)
        printf("老年\n");
    else
        printf("老寿星\n");
    return 0;
}

(3)语句书写的注意点

        比如你写了下面这么一段代码:

#include <stdio.h>
int main()
{
    int a = 0;
    int b = 2;
    if(a == 1)
        if(b == 2)
            printf("hehe\n");
    else
        printf("haha\n");
    return 0;
}

        似乎在这里应该输出“haha”,但如果运行起来会发现并没有打印任何字符。这正是因为else的匹配机制,使得代码中的else虽然排列成了与"if(a == 1)"平行的样子,但本质上还是与”if(b == 2)"相匹配的,所以这里的在"a == 1"出判断为假就直接跳过整个条件语句运行"return 0"了。

        那要怎么修改这段代码才能在不改变a、b取值的情况下输出“haha”呢?

#include <stdio.h>

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

        由这两段代码我们可以发现,else总是和他离得最近的if匹配,只有通过代码块{  }加以限制才能达到前文所想要的输出“haha”的效果。同时在写代码的时候也应当注意适当地使用代码块{  },这可以使代码的逻辑更加清晰,有助于养成良好的写代码习惯,形成良好的代码风格。

        下面会通过两组代码展现条件语句中逻辑的重要性:

//代码1
if (condition)
{
    return x;
}
return y;

//代码2
if (condition)
{
    return x;
}
else
{
    return y;
}

        上面这组代码其实表达出来的意思都是一样的——如果满足condition的条件就返回x,反之返回y。但是代码2逻辑更加清晰,让人一目了然这段代码的目的,不容易出错,所以更推荐代码2的写法。

//代码1
int num = 1;
if(num == 5)
{
    printf("hehe\n");
}

//代码2
int num = 1;
if(5 == num)
{
    printf("hehe\n");
}

        上面这组代码同样也表达相同意义——如果num为5就打印“hehe”。但是代码2可以有效避免因少打一个等号将判断变成赋值时依旧打印“hehe”的情况。如果是代码1,“num==5”变成“num=5”时,由于num被赋值为5,那语句可以等同于“if(5)”,这时恒为真,无论如何都会打印“hehe”;如果是代码2,“5==num”变成“5=num”时会因为常数不可赋值时报错,从而有效检查进而避免错误。

        下面有一个练习,大家可以思考一下,尝试写出相应代码。

        练习:判断一个数是否为奇数

//参考答案

#include <stdio.h>

int main()
{
    int a = 0;
    printf("判断你输入的数字是否为奇数?\n");
    scanf("%d\n",&a);
    if(0 == (a%2))
        printf("不是\n");
    else
        printf("是\n");
    return 0;
}

  2、Switch语句

(1)语法结构

switch(整形表达式)
{
    语句项;
}

//对语句项的解释
//语句项是一些case语句,比如:

case 整型常量/枚举常量表达式
    语句;

(2)使用情境

        switch语句多用于多分支情况。

        比如:输入n,输出星期n,若写成“if...else if”的形式过于复杂,此时用不同语法形式更方便。

(3)break语句的使用

        在switch语句中,我们没办法直接实现分支,搭配break语句的使用才能实现真正的分支。本质上,break语句的实际效果是把语句列表划分为不同的分支部分。其中,case语句决定的是分支的入口,而break语句决定的是分支的出口,故在没有break语句的情况下,case语句加载后会一直向下读取,直到break语句或switch语句结束为止。

        所以,对于上面的例子,代码的写法如下:

#include <stdio.h>
int main()
{
    int day = 0;
    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;
}

        有时候,我们的需求变了,需要区分工作日和周末时,代码便可以这么写:

#include <stdio.h>

int main()
{
    int day = 0;
    switch(day)
   {
        case 1://在遇到break前会一直执行下去
        case 2:
        case 3:
        case 4:
        case 5:
            printf("weekday\n");
            break;
        case 6:
        case 7:
            printf("weekend\n");
            break;
   }
    return 0;
}

        这里需要知道一个编程的好习惯——习惯于在最后一个case语句的后面加上break语句(由此可以避免出现在以前的最后一个case语句后面忘记添加case语句导致代码运行起来出现偏差的情况)

(4)default子句的使用

        如果上文代码中day不属于case标签的值,无法匹配怎么办呢?其实也就是所有的语句都被跳过了而已,程序并不会因此中止,也不会报错,因为C中不认为这个情况属于错误

        这时,如果不想忽略不匹配所有标签的表达式,就需要用到一条“default子句”,将标签“default:”写在任何一个case标签可以出现的位置,当switch表达式的值并不匹配所有case标签的值时,这个default子句后面的语句就会执行。

        因此我们不难发现,每个switch语句中只能出现一条default子句,但是他可以出现在语句列表中的任意位置,且语句流会像执行一个case标签一样执行default子句。

        这里需要知道一个编程的好习惯——在每个switch语句中都放一条default子句,甚至可以在后面再加入一个break。

        下面有一个练习,大家可以思考一下,由此也可以体会default子句在switch语句中的使用。

//求m、n的值
#include <stdio.h>

int main()
{
    int n = 1;
    int m = 2;

    switch(n)
    {
    case 1:
        m++;
    case 2:
        n++;
    case 3:
        switch(n)//switch语句可以嵌套使用
        {
        case 1:
            n++;
        case 2:
            m++;
            n++;
            break;
        }
    case 4:
        m++;
        break;
    default:
        break;
    }

    printf("m = %d,n = %d\n",m,n);
    return 0;
}


//参考答案:m = 5,n = 3.

三、循环语句(循环结构)

1、while语句

        正如上图所示,生活中,很多事情需要多次完成,在C语言中如何实现这种循环呢?其中while语句就是一种实现形式。

(1)语法结构

//语法结构
while(表达式)
    循环语句;

(2)执行流程

         下面有一个代码可以帮我们更好的了解while语句的基本语法:

//在屏幕上打印1-10的数字。
#include <stdio.h>

int main()
{
    int i = 1;
    while(i <= 10)
    {
        printf("%d  ",i);
        i++;//(i = i + 1)
    }
    return 0;
}

(3)while语句中的break和continue

        ①break介绍

        直接上代码实例:

#include <stdio.h>

int main()
{
    int i = 1;

    while(i <= 10)
    {
        if(i == 5)
            break;
        printf("%d ",i);
        i++;
    }

    return 0;
}

        这里代码的输出结果会是什么呢?

        不难发现,在i不断累加到5时,"if(i == 5)"成立,执行break指令,故应输出“1 2 3 4 ”

        总结:break在while语句中的作用:只要遇到break就永久终止所有循环。

        ②continue介绍

        同样直接上代码:

//实例1
#include <stdio.h>

int main()
{
    int i = 1;
    while(i <= 10)
    {
        if (i == 5)
            continue;
        printf("%d ",i);
        i++;
    }
    return 0;
}

//实例2
#include <stdio.h>

int main()
{
    int i = 1;
    while(i <= 10)
    {
        i++;
        if (i == 5)
            continue;
        printf("%d ",i);
    }
    return 0;
}

        两个实例的输出结果是什么呢?

        实例1的输出结果为“1 2 3 4 ”,其中4后面的光标仍然在闪动,说明程序仍然在运行。这是因为在“if(i == 5)”时,执行“continue”语句直接结束了当次循环,故i的值恒为5,形成死循环。

        实例2的输出结果为“2 3 4 6 7 8 9 10 11 ”。在“if(i == 5)”时,执行“continue”语句跳过了打印环节,直接进入下一次循环,此时i的值变成6继续打印。

        总结:continue在while语句中的作用:只要遇到continue就终止本次循环,本次循环的后面代码不再执行,直接跳转至while语句的判断部分进行下一次循环入口判断。

        接下来再看几个代码:

//代码1
#include <stdio.h>
int main()
{
    char ch = 0;
    while ((ch = getchar()) != EOF)
        putchar(ch);
    return 0;
}

        **对代码1的部分内容进行一些解释:

        ①对scanf函数而言,读取成功后返回的是读取到的数据个数;而对getchar函数来说,读取成功后返回的是ASCII码值。

        ②EOF(End of File)的用法有如下两种:

        Ⅰ.若定义“#define EOF”,此时EOF的值默认为-1

        Ⅱ.若在函数读取失败时,便会返回EOF

        ③((ch = getchar()) != EOF)语句的意思是:如果输入的值读取不失败的情况下进入循环。

        ④putchar(ch)语句用于打印字符。

//代码2——打印数字字符,跳过其他字符
#include <stdio.h>

int main()
{
    char ch = '\0';
    while((ch = getchar() != EOF))
    {
        if(ch < '0' || ch > '9')
            continue;
        putchar(ch);
    }
    return 0;
}

2、for语句

(1)语法结构

for(表达式1;表达式2;表达式3)
    循环语句;

表达式1:初始化部分,用于初始化循环变量。
表达式2:条件判断部分,用于判断循环是否进行。
表达式3:调整部分,用于循环条件的调整。

        接下来用for循环解决一个实际问题,读者可以体会一下for循环语句的使用:

//在屏幕上打印1-10的数字
#include <stdio.h>
int main()
{
 int i = 0;
 for(i=1; i<=10; i++)
 {
 printf("%d ", i);
 }
 return 0;
}

(2)执行流程

(3)for语句中的break和continue

        for循环中一样可以出现break和continue,其意义与在while循环中一致。

        看下面两个代码:

//代码1
#include <stdio.h>
int main()
{
 int i = 0;
 for(i=1; i<=10; i++)
 {
 if(i == 5)
 break;
 printf("%d ",i);
 }
 return 0;
}

//代码2
#include <stdio.h>
int main()
{
 int i = 0;
 for(i=1; i<=10; i++)
 {
 if(i == 5)
 continue;
 printf("%d ",i);
 }
 return 0;
}

 (4)for循环中的循环控制变量

        下面有两个关于循环控制变量的建议:

        ①不可再for循环体内修改循环变量,防止for循环因此失去控制;

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

//前闭后开的写法
for(i=0; i<10; i++)
{}

//两边都是闭区间
for(i=0; i<=9; i++)
{}

(5)for循环的部分变种

#include <stdio.h>
int main()
{
    //代码1
    for(;;)
        printf("hehe\n");
    //变种1:省略for循环中的初始化部分,判断部分,调整部分
    //其中初始化部分和调整部分在合理省略下依旧可以正常运行,但如果省略判断部分,则for循环
    //判断条件时恒为真,会陷入死循环。
    //但是不建议初学时省略,容易导致问题。

    //代码2
    int i = 0;
    int j = 0;

    for(i=0; i<10; i++)
    {
        for(j=0; j<10; j++)//变种2:循环可以嵌套循环
        {
            printf("hehe\n");//这里打印多少个hehe?(100个)
            //省略掉初始化部分,这里打印多少个hehe?(在第一次j变成10后由于没有初始化
            //会一直为10,故只打印10个hehe)
            //变种3:省略初始化
        }
    }

    
    //代码3
    int x, y;
    for (x = 0, y = 0; x<2 && y<5; ++x, y++)//变种4:使用多于一个变量控制循环
    {
         printf("hehe\n");
    }
    return 0;
}

        下面有一题例题,读者可以独立思考一下:

//请问循环要循环多少次?
#include <stdio.h>

int main()
{
    int i = 0;
    int k = 0;
    for(i =0,k=0; k=0; i++,k++)
        k++;
    return 0;
}


//参考答案:0次(k=0返回“0”,即条件判断恒为假,不会进入循环)

3、do...while()语句

(1)语法结构

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

(2)执行流程

(3)do...while语句中的break和continue

        do...while循环中一样可以出现break和continue,其意义与在while循环中一致。

        看下面两个代码:

//代码1
#include <stdio.h>
int main()
{
    int i = 10; 

    do
    {
        if(5 == i)
            break;
        printf("%d\n", i);
    }while(i<10);
    
    return 0;
}

//代码2
#include <stdio.h>
int main()
{
    int i = 10;
    
    do
    {
    if(5 == i)
        continue;
    printf("%d\n", i);
    }while(i<10);
    
    return 0;
}

(4)语句特点

        该循环至少会执行一次,故使用场景往往有限,并不经常使用。

4、goto语句

        在C语言中有提供可以随意滥用的goto语句和标记跳转的记号,从理论来说goto语句并没有必要,从实践来说就算没有goto语句也可以很好的写出代码,但是某些场合下还是可以使用的——最常见的用法就是终止程序在某些深度嵌套的结构处理过程(比如一次跳出两层以上的循环)

        goto语言真正适合的的场景如下:

for(...)
{
    for(...)
    {
        for(...)
        {
            if(disaster)
                goto error;
        }
    }
    …
}

error:
    if(disaster)    // 处理错误情况
    ……

        下面是使用goto语句的一个例子:

//用goto语句实现一个关机程序
#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;
}

        但如果不是用goto语句,也可以通过循环书写该代码:

#include <stdio.h>
#include <stdlib.h>

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

四、练习

        下文将给出几个练习,大家可以尝试书写代码,检测该章学习的成果。

1、计算n的阶乘

        对于这个问题,首先我们需要理解n的阶乘是如何计算的。n的阶乘在本质上就是将1到n之间的所有整数做乘法,所以只需要生成一个不断累加的整数i,让i从1加到n,再将这些值相乘即可。

        代码实现如下图所示:

#include <stdio.h>

int main()
{
    int i = 0;
    int n = 0;
    int num = 1;

    scanf("%d\n",&n);
    
    for(i = 1;i <= n;i++)
    {
        num *= i;
    }

    printf("%d! = %d", n,num);
    return 0;
}

2、计算“1!+2!+3!+···+10!”

        对于这个问题,前面我们已经实现了n的阶乘,本题将n限定在了1~10,从一个固定数变成了一个变化数,故只需要让n也不断变化,同时用一个变量储存每个阶乘的结果的和即可。

        代码实现如下图所示:

#include <stdio.h>

int main()
{
    int i = 0;
    int n = 0;
    int num = 0;
    int sum = 0;

    for(n = 1;n <= 10;n++)
//如果10不确定,只需要再创建一个变量m,将10改成m,并加入输入m的代码即可
    {
        for(i = 1;i <= n;i++)
        {
            num *= i;
        }
        sum += num;
    }
    
    printf("%d\n",sum);
    return 0;
}

3、在一个有序数组中查找具体的某个数字n

        在这之前, 首先读者需要知道“二分查找”的概念。

        对于一串很长的信息,如果能从中间分成两半进行查找,可以节省不少时间和精力。

        下面将展示在主函数中实现二分查找:

int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,10};//查找的内容必须有序
    int left = 0;//最左端位置
    int right = sizeof(arr)/sizeof(arr[0])-1;//最右端位置
    int key = 7;//需要查找的数
    int mid = 0;//中间数
    while(left<=right)
    {
        mid = (left+right)/2;//可能会发生截断
        //优化:mid = left + (right - left) / 2;
        if(arr[mid]>key)
        {
            right = mid-1;//如果arr[mid]比key大,说明key在mid的左端
            //所以直接将right移动到mid处,由于数组命名关系,这里需要-1对齐
        }
        else if(arr[mid] < key)
        {
            left = mid+1;//如果arr[mid]比key小,说明key在mid的右端
            //所以直接将left移动到mid处,由于数组命名关系,这里需要+1对齐
        }
        else
            break;
    }
    if(left <= right)//在该情况下如果结束了循环,就说明找到了,终止了循环。
        printf("找到了,下标是%d\n", mid);
    else
        printf("找不到\n");
}

        同理,也可以通过函数的形式实现一下二分查找,从而方便多次调用:

int bin_search(int arr[], int left, int right, int key)
{
    int mid = 0;
    while(left<=right)
    {
        mid = left + (right - left) / 2;
        if(arr[mid]>key)
        {
            right = mid-1;
        }
        else if(arr[mid] < key)
        {
            left = mid+1;
        }
        else
            return mid;//找到了,返回下标
    }    
    return -1;//找不到
}

4、编写代码,演示多个字符从两端移动,向中间汇聚

//编写代码,演示多个字符从两端移动,向中间汇聚
#include <stdio.h>

int main()
{
    char arr1[] = "welcome to csdn...";
    char arr2[] = "#################";
    int left = 0;
    int right = strlen(arr1)-1;
    printf("%s\n", arr2);//利用arr2数组演示出逐个打印的过程

    //while循环实现
    while(left<=right)
    {
        Sleep(1000);//sleep函数确实让代码有所停留,让人感觉有一种打印的过程(单位:毫秒)
        arr2[left] = arr1[left];
        arr2[right] = arr1[right];
        left++;
        right--;
        printf("%s\n", arr2);
    }

    //for循环实现
    for (left=0, right=strlen(src)-1; left <= right; left++, right--)
    {
        Sleep(1000);
        arr2[left] = arr1[left];    
        arr2[right] = arr1[right];
        printf("%s\n", arr2);
    }

    retutn 0;
}

5、编写代码,实现模拟用户登录情景,并且只能登录三次,均输入错误直接退出程序

int main()
{
    char psw[10] = "" ;
    int i = 0;
    int j = 0;
    for (i = 0; i < 3 ; ++i)
    {
        printf( "please input:");
        scanf("%s", psw);
        if (strcmp(psw, "password" ) == 0)
            break;
    }
    if (i == 3)
        printf("exit\n");
    else
        printf( "log in\n");
}

        除此之外,还可以借由条件和循环语句实现猜数字游戏,这里不再展开,读者可自行练习。


本章完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值