分支语句与循环语句

1. 什么是语句?

C语言中语句可分为以下 5 类:

  1. 表达式语句
  2. 函数调用语句
  3. 控制语句
  4. 复合语句
  5. 空语句

控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成,C语言有 9 种控制语句。

可分为以下三类:

  1. 条件判断语句也叫分支语句:if 语句、switch 语句;
  2. 循环执行语句:do while 语句、while 语句、for 语句;
  3. 转向语句:break 语句、goto 语句、continue 语句、return 语句。

2. 分支语句(选择结构)

2.1 if 语句

作用:执行满足条件的语句

那 if 语句的语法结构是怎么样的呢?

语法结构:
单行格式:
if (表达式)
    语句;

多行格式:
if (表达式)
    语句1;
else
    语句2;

多分支(多条件):
if (表达式1)
    语句1;
else if (表达式2)
    语句2;
else
    语句3;

如果表达式结果为真,则语句执行。

在C语言中如何表示真假?

0表示假,非0表示真 。

如果条件成立,要执行多条语句,需要使用代码块。

if (表达式) {
    语句列表1;
} 
else {
    语句列表2;
}

这里的一对 {  } 就是一个代码块。

嵌套 if 语句:在 if 语句中,可以嵌套使用 if 语句,达到更精准的条件判断

2.1.1 悬空 else

当你写了这个代码:

#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 的匹配是就近原则,else 是和它离得最近的 if 匹配的。

改正:

#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;
}

代码风格很重要,适当使用 {  } 可以使代码的逻辑更加清楚。

2.1.2 if 书写形式的对比

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

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

//代码3
int num = 1;
if (num == 5) {
    printf("hh");
}

//代码4
int num = 1;
if (5 == num) {
    printf("hh");
}

代码2和代码4更好,逻辑更加清晰,不容易出错。

2.2 switch 语句

switch 语句常常用于执行多条件分支的情况。

语法结构:
switch (整型表达式) 
{
case 整型常量表达式1:
    语句1;
    break;
case 整型常量表达式2:
    语句2;
    break;
case 整型常量表达式3:
    语句3;
    break;
default:
    break;
}

注意:字符也是整型常量。 

switch 语句特点:

  • 缺点:判断的时候只能是整型(包括字符型),不可以是一个区间
  • 优点:结构清晰,执行效率高 

2.2.1 在 switch 语句中的 break

在 switch 语句中,我们没办法直接实现分支,搭配 break 使用才能实现真正的分支。如果 case 里没有 break,那么程序会一直向下执行。

break 语句 的实际效果是把语句列表划分为不同的分支部分。

编程好习惯:

在最后一个 case 语句的后面加上一条 break 语句。

(之所以这样写是可以避免出现在以前的最后一个 case 语句后面继续写case语句时忘记添加 break 语句)

2.2.2 default 子句

如果表达的值与所有的case标签的值都不匹配怎么办?

其实也没什么,结构就是所有的语句都被跳过而已。程序并不会终止,也不会报错,因为这种情况在C中并不认为时错误。

但是,如果你并不想忽略不匹配所有标签的表达式的值时该怎么办呢?

你可以在语句列表中增加一条 default 子句,把 default:写在任何一个case标签可以出现的位置。

当 switch 表达式的值并不匹配所有 case 标签的值时,这个 default 子句后面的语句就会执行。

所以,每个 switch 语句中只能出现一条 default 子句。

​​​但是它可以出现在语句列表的任何一个位置,而且语句流会像执行一个 case 标签一样执行 default 子句。

编程好习惯:

在每个 switch 语句中都放一条 default 子句是一个好习惯,甚至可以在后面再加一个 break。

2.2.3 switch 允许嵌套使用

#include <stdio.h>

int main() {
    int n = 1;
    int m = 2;
    switch (n) {
    case 1:m++;
    case 2:n++;
    case 3:
        switch (n) {
        case 1:n++;
        case 2:m++; n++;
            break;
        }
    case 4:
        m++;
        break;
    default:
        break;
    }
    printf("m = %d, n = %d", m, n);

    return 0;
}

3. 循环语句

  • while
  • for
  • do while

3.1 while 循环

由于我们发现生活中很多的实际的例子是:同一件事情需要我们完成很多次。那我们怎么做呢?

C语言中给我们引入了:while 语句,可以实现循环。

作用:满足循环条件,执行循环语句

语法结构

while (表达式)
    循环语句;

解释:只要循环条件的结果为真,就执行循环语句。

比如我们实现:

在屏幕上打印1~10的数字。

#include <stdio.h>

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

注意:在执行循环语句的时候,程序必须提供跳出循环的出口,否则出现死循环。

3.1.1 while 语句中的 break 和 continue

break 介绍

break 在 while 循环中的作用:

其实在循环中只要遇到了 break,就停止后期的所有的循环,直接终止循环。

所以:while 中的 break 是用于永久终止循环的。

#include <stdio.h>

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

continue 在 while 循环中的作用:

其实在循环中只要遇到了 continue,就会跳过当前循环的后续所有内容,直接去判断部分,进入下一次循环的判断。

所以:while 中的 continue 是用于跳过指定循环的。

#include <stdio.h>

int main() {
    int i = 1;
    while(i <= 10) {
        i = i + 1;

        if (5 == i) {
            continue;
        }

        printf("%d ", i);
    }
    return 0;
}

注意:i = i + 1;先执行,会对后续代码的结果产生影响。

3.1.2 while 循环的使用

  • 使用 while 循环可以用来清理缓冲区。

首先,介绍以下 getchar() 和 putchar() 函数的使用。

#include <stdio.h>

int main() {
    int ch = 0;
    while ((ch = getchar()) != EOF) {
        putchar(ch);
        //printf("%c", ch);
    }

    return 0;
}
  • getchar():获取字符的 Ascii 码值。当读取错误或者读取到文件末尾时,返回EOF。
  • putchar():以字符的形式打印 变量的值。

接下来,考虑一个读取密码的案例。

#include <stdio.h>

int main() {
    char pw[20] = { 0 };
    printf("请输入密码:");
    scanf("%s", pw);

    getchar();//读取了'\n'

    printf("请确认密码(Y/N):>");
    int ret = getchar();
    if ('Y' == ret) {
        printf("Yes\n");
    }
    else {
        printf("No\n");
    }

    return 0;
}

注:

  • 在输入 "abc",按下回车后 在输入缓冲区的内容为 "abc\n";
  • scanf() 函数读到 空格/ '\n' 停止,并且只读取有效地字符串,会遗留 空格及后续字符/ '\n' 在输入缓冲区中。

改正:

#include <stdio.h>

int main() {
    char pw[20] = { 0 };
    printf("请输入密码:");
    scanf("%s", pw);

    int ch = 0;
    while ((ch = getchar()) != '\n') {
        ;
    }

    printf("请确认密码(Y/N):>");
    int ret = getchar();
    if ('Y' == ret) {
        printf("Yes\n");
    }
    else {
        printf("No\n");
    }

    return 0;
}
  • 使用 while 循环只打印数字字符,跳过其他字符。
#include <stdio.h>

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

    return 0;
}

3.2 for 循环

我们已经知道了 while 循环,但是我们为什么还要一个 for 循环呢?

作用:满足循环条件,执行循环语句

首先来看看 for 循环的语法:

3.2.1 语法

for (表达式1, 表达式2, 表达式3)
    循环语句;
  • 表达式1:初始化部分,用于初始化循环变量。
  • 表达式2:条件判断部分,用于判断循环是否终止。
  • 表达式3:调整部分,用于循环条件的调整。

实际问题

使用 for 循环 在屏幕上打印 1-10 的数字。

#include <stdio.h>

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

    return 0;
}

3.2.2 for 语句中的 break 和 continue

我们发现在 for 循环中也可以出现 break 和 continue,他们的意义和在 while 循环中是一样的。

但是还是有些差异:

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

        printf("%d ", i);
        i = i + 1;
    }
    return 0;
}

//代码2
#include <stdio.h>
int main() {
    for (int i = 1; i <= 10; i++) {
        if (5 == i) {
            continue;
        }
        printf("%d ", i);
    }
    return 0;
}
  • 在 while 循环中,continue 会跳过本次循环的后续部分,直接进入下次循环的条件判断部分,可能略过了循环的条件调整部分造成死循环
  • 在 for 循环中,continue 会跳过本次循环的后续部分,直接进入下次循环的条件调整部分,再进入条件判断部分。

3.2.3 for 语句的循环控制变量

建议:

  1. 不可在 for 循环体内修改循环变量,防止 for 循环失去控制。
  2. 建议 for 语句的循环控制变量的取值采用“前闭后开区间”写法。
int i = 0;
//前闭后开的写法
for (i = 0; i < 10; i++) {
    ;
}

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

3.2.4 一些 for 循环的变种

  • for 循环中的初始化部分,判断部分,调整部分是可以省略的。
for (;;) {
    printf("hehe\n");
}

for 循环的判断部分省略意味着判断会恒成立。

  • C99 语法才支持了在 for 循环的初始化部分 初始化循环变量,编译器不一定支持。(C++支持这种写法)
for (int i = 0; i < 10; i++) {
    printf("hehe\n");
}
  • 使用多个变量控制循环
int x, y;
for (x = 0, y = 0; x < 2 && y < 5; ++x, y++) {
    printf("hehe\n");
}
  • 使用嵌套的 for 循环
int i = 0;
int j = 0;

for (i = 0; i < 10; i++) {
    for (j = 0; j < 10; j++) {
        printf("hehe\n");
    }
}

注意:嵌套 for 循环的初始化部分不要省略。

测试题:请问循环要循环多少次?

#include <stdio.h>

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

赋值 k = 0; 0 为假,不进入循环。

3.3 do...while() 循环

作用:满足循环条件,执行循环语句

3.3.1 do语句的语法

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

与 while 的区别在于 do...while 会先执行一次循环语句,在判断循环条件。

3.3.2 do语句的特点

循环至少执行一次,使用的场景有限,所以不是经常使用。

3.3.3 do 语句中的 break 和 continue

意义和在 while 循环中是一样的。

3.4 嵌套循环

作用:在循环体中再嵌套一层循环,解决一些实际问题

示例:打印一个正方形图案。

#include <stdio.h>

int main() {
	int i = 0;
	int n = 0;
	scanf("%d", &n);
	for (i = 0; i < n; i++) {
		int j = 0;
		for (j = 0; j < n; j++) {
			printf("* ");
		}
		printf("\n");
	}

	return 0;
}

3.4 练习

  1. 计算 n 的阶乘。
  2. 计算 1! + 2! + 3! +...+10! 。
  3. 编写代码,演示多个字符从两端移动,向中间汇聚。
  4. 编写代码,模拟用户登录场景,只允许输入三次密码,如果密码正确则提示登陆成功,如果三次均输入错误,则退出程序。

练习参考代码:

//代码1
#include <stdio.h>
int main() {
    int sum = 1;
    int i = 1;
    int n = 0;
    scanf("%d", &n);

    for (i = 1; i <= n; i++) {
        sum *= i;
    }

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

//代码2
#include <stdio.h>
int main() {
    int sum = 1;
    int i = 1;
    int ret = 0;

    for (i = 1; i <= 10; i++) {
        sum *= i;
        ret += sum;
    }

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

//代码3
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
int main() {
    char arr1[] = "welcome to abc!!!!";
    char arr2[] = "##################";

    int left = 0;
    int right = strlen(arr1) - 1;

    while (left <= right) {
        arr2[left] = arr1[left];
        arr2[right] = arr1[right];
        printf("%s\n", arr2);
        Sleep(1000);
        //清空屏幕
        system("cls");//system是一个库函数,可以执行系统命令
        left++; right--;
    }

    return 0;
}

//代码4
#include <stdio.h>
#include <string.h>

int main() {
    int i = 0;
    char pw[20] = { 0 };

    for (i = 0; i < 3; i++) {
        printf("请输入密码:>");
        scanf("%s", pw);
        if (strcmp(pw, "abcdef") == 0) {
            printf("登陆成功\n");
            break;
        } else {
            printf("密码错误\n");
        }

    }
    
    if (i == 3) {
        printf("三次密码均输入错误,退出程序\n");
    }

    return 0;
}

Sleep():将进程挂起一段时间。在头文件 windows.h 中。

 system():执行系统命令的库函数。在头文件 stdlib.h 中。

  •  比较两个字符串是否相等,不能使用==,而应该使用一个库函数:strcmp。
  • strcmp():如果返回值是 0,表示 2 个字符串相等。

4. goto 语句

作用:可以无条件跳转语句

C语言中提供了可以任意滥用的 goto 语句和标记跳转的标号。

语法结构
again:
    代码块;
    goto again;

解释:如果标记的名称存在,执行到 goto 语句时,会跳转到标记的位置。 

goto 语句不能跨函数跳转。

从理论上 goto 语句是没有必要的,实践中没有 goto 语句也可以很容易的写出代码。

但是某些场合下 goto 语句还是用得着的,最常见的用法就是终止程序在某些深层嵌套的结构的处理过程

例如:一次跳出两层或者多层循环。(多层循环这种情况使用 break 是达不到目的的。它只能从内层循环退出到上一层循环。)

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 分钟内关机,如果输入:666,就取消关机!\n请输入:>");
    scanf("%s", input);
    if (0 == strcmp(input, "666")) {
        system("shutdown -a");
    }
    else {
        goto again;
    }
    return 0;
}
#include <stdio.h>
int main() {
    char input[10] = 0;
    system("shutdown -s -t 60");

    while(1) {
        printf("电脑将在 1 分钟内关机,如果输入:666,就取消关机!\n请输入:>");
        scanf("%s", input);
        if (0 == strcmp(input, "666")) {
            system("shutdown -a");
            break;
        }
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值