Lesson2:分支语句与循环语句

一、前言

在我们的人生道路上,基本上逃不出这三种情况:一直往前走(顺序结构)、在分岔口上进行人生中重要的抉择(选择结构)、在一个周期内日复一日的重复一件事(循环结构)。

我们将这三种结构引入到C语言的编程当中,所以C语言是一门结构化的程序设计语言

由于顺序语句就是从上往下按顺序执行,易于理解,故本章并未对其进行详细介绍。本章主要介绍分支语句和循环语句,另外再介绍C语言当中的goto语句。

二、本章重点

本章重点学习分支语句和循环语句的使用。

分支语句(选择结构)循环语句(循环结构)goto语句
if
switch
while
for
do while
goto

三、什么是语句?

C语言中由一个分号  ;  隔开的就是一条语句。比如:

int main()
{
	;  // 空语句
	1 + 2;  // 语句
	printf("hehe\n");  // 语句
	return 0;  // 语句
//}

Note:只有一个 ; 的语句也是一条语句,为空语句,在特殊场景下使用。

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

如果你好好学习,校招时拿一个好offer,走上人生巅峰。

如果你不学习,毕业等于失业,回家卖红薯。

这就是选择!

1.if语句

①语法结构

// 单分支
if (表达式)  // 如果表达式为真(非0),执行语句;如果表达式为假(0),不执行语句
    语句;

// 双分支
if (表达式)  // 如果表达式为真(非0),执行语句1;如果表达式为假(0),执行语句2
    语句1;
else
    语句2;

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

Note:如果表达式的结果为真,则语句执行。在C语言中,0为假,非0为真。

比如:

// 代码1(单分支)
int main()
{
	int age = 0;
	scanf("%d", &age);
	if (age < 18)
		printf("未成年\n");
	return 0;
}

// 代码2(双分支)
int main()
{
	int age = 0;
	scanf("%d", &age);
	if (age < 18)
		printf("未成年\n");
	else
		printf("成年\n");
	return 0;
}

// 代码3(多分支)
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;
}

★Question:代码3中的条件表达式(age >= 18 && age < 30)写成(18 <= age < 30)可以吗?

★Answer:不可以。若age输入40,我们会发现终端输出青年,和我们的预期结果不一样。

(18 <= age < 30),程序先执行表达式左半部分18 <= age,条件为真(返回非0),表达式左半部分赋值为1;程序再执行表达式右半部分(1 < 30),条件为真。

(18 <= age < 30)表达式为真,该判断条件下的语句执行,终端输出青年。这是错误代码。

此时需要借助逻辑与操作符&&来帮助我们判断表达式真假,即(age >= 18 && age < 30)。这样当age输入40时,终端才会输出我们的预期结果——中年。

如果条件成立,要执行多条语句,则应该使用代码块 {}

int main()
{
	if (表达式)
	{
		语句列表1;
	}
	else
	{
		语句列表2;
	}
	return 0;
}

比如:

// 代码1
int main()
{
	int age = 0;
	scanf("%d", &age);
	if (age > 18)  // 未使用{},if语句只能控制第一条语句
		printf("成年\n");
	    printf("可以谈恋爱\n");  // 此句代码不受if语句控制
	return 0;
}

// 代码2
int main()
{
	int age = 0;
    scanf("%d", &age);
	if (age > 18)  // {}使得两条语句属于同一个逻辑,且代码阅读效果更好
	{
		printf("成年\n");
		printf("可以谈恋爱\n");  // 若想此句代码块受if语句控制,需使用{}
	}
	return 0;
}

②悬空else

当你写了这个代码:

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是与第二个if进行匹配。

Note:else默认与距离最近且未匹配的if进行匹配(就近原则),而不是else和哪个if对齐就和哪个if进行匹配。

改正:

// 适当的使用{}可以使代码的逻辑更加清楚,代码风格很重要
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与第二个if距离最近,但是是与第一个if进行匹配。

③if书写形式的对比

请大家将下面的代码1与代码2比较阅读,代码3和代码4比较阅读,并评价哪种代码风格更优?

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

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

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

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

代码1是条件成立返回x,条件不成立返回y吗?

答案是肯定的。但不易看懂,可读性太差,写成代码2的风格会更好一些

代码3如果条件判断句(num == 5)误写成(num = 5),代码还会按预期结果运行吗?

答案是否定的。(num = 5)是将5赋值给num,5为非0,条件判断始终为真,终端始终输出5,且不会报错。因此在比较常量和变量相等的时候,写成代码4的风格会更好一些,即将常量放在左边。代码4即使误写成(5 = num),也易于检查。因为变量num的值不能赋值给常量,编译都无法通过。

Note:代码风格非常重要,要养成良好的代码风格。在这里给大家推荐一本林锐博士编写的书籍《高质量程序设计指南——C++/C语言》,该书中详细介绍了如何规范地写代码。

④练习

<1>判断一个数是否为奇数

#include <stdio.h>
int main()
{
	int num = 0;
	scanf("%d", &num);
	if (num % 2 == 1)
	{
		printf("%d是奇数\n", num);
	}
	else
	{
		printf("%d不是奇数\n", num);
	}
	return 0;
}

<2>输出1~100之间的奇数

#include <stdio.h>
int main()
{
	int num = 1;
	while (num <= 100)
	{
		if (num % 2 == 1)
		{
			printf("%d ", num);
			num++;
		}
		else
		{
			num++;
		}
	}
	return 0;
}

2.switch语句

①语法结构

switch语句也是一种分支语句。 常常用于多分支的情况。

比如:

  • 输入1,输出星期一
  • 输入2,输出星期二
  • 输入3,输出星期三
  • 输入4,输出星期四
  • 输入5,输出星期五
  • 输入6,输出星期六
  • 输入7,输出星期七

写成if……else if……else if……的形式太复杂,那我们就得有不一样的语法形式。这就是switch语句。

switch (整型表达式)
{
case 整形常量表达式:
    语句;
}

Note:switch后面跟整型表达式,case后面跟整型常量表达式。

②在switch语句中的break

在switch语句中,我们没法直接实现分支。搭配break使用才能帮助我们跳出switch语句,实现真正的分支。

比如:

// 代码1
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;
}

// 代码2
int main()
{
	int day = 0;
	scanf("%d", &day);
	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;
}

代码1中当我们输入5时,终端输出星期五星期六星期天,此时我们已经看到了分支的影子,但貌似还并没有像我们预期的那样实现真正的分支。

代码2是实现分支的正确代码,需要搭配break使用。当我们输入5时,终端输出星期五,是我们预期的结果。通过case语句进入预期的分支,通过break语句跳出switch分支语句。

Note:case类似于分支语句的入口,进入我们预期的分支;break类似分支语句的出口,跳出switch分支语句。两者需搭配相互使用。

Note:我们要注意switch、case表达式的使用条件。

  • 如果day用float定义(float day = 0;),switch (day)是错误代码,day必须是整型表达式。
  • case 1.0是错误代码,case后面必须跟整型常量表达式。
  • int n = 1; case n是错误代码,case后面必须跟整型常量表达式。
  • case 1 + 0是正确代码,1 + 0是整型常量表达式。

有时候我们的需求变了:当输入1~5时,终端输出“weekday”;当输入6~7时,终端输出“weekend”。

所以我们的代码就应该这样实现了:

int main()
{
	int day = 0;
	scanf("%d", &day);
	switch (day)
	{
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
		printf("weekday\n");
		break;
	case 6:
	case 7:
		printf("weekend\n");
		break;
	}
	return 0;
}

case语句里面的break并非必须使用,要根据逻辑来选择加不加break;但是switch-case语句里面绝对不能出现continue。

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

Note:在这里给大家介绍一个编程的好习惯,每次写switch语句时,最好在最后一个case语句里面加上一条break语句

虽然最后一个case语句里面不加break语句也能正常走出分支结构,但是笔者的推荐写法可以避免出现在以前的最后一个case语句后面忘了添加break语句,后续再添加case语句时会产生bug的现象。

③default子句

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

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

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

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

default:

当 switch表达式的值并不匹配所有case标签的值时,这个default子句后面的语句就会执行。所以,每个switch语句中只能出现一条default子句。它可以出现在语句列表的任何位置,而且语句流会像贯穿一个case标签一样贯穿default子句。

比如:

int main()
{
	int day = 0;
	scanf("%d", &day);
	switch (day)
	{
	default:
		printf("输入数据有误,请输入1-7的数字\n");
		break;
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
		printf("weekday\n");
		break;
	case 6:
	case 7:
		printf("weekend\n");
		break;
	}
	return 0;
}

Note:在这里给大家介绍一个编程的好习惯,在每个switch语句中都放一条default子句,甚至可以在后边再加一个break

④练习

<1>阅读下面代码,程序终端输出的结果是什么?

#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。

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

如果你买彩票没有中就老实学习,老实学习还是菜鸟就买彩票,买彩票没有中就老实学习,老实学习还是菜鸟就买彩票……

这就是循环!

日复一日年复一年,当你有朝一日买彩票中了500万,或者成为业界的大牛,就不用继续买彩票和老实学习,直接迎娶白富美。

这就是跳出循环!

1.while循环

①语法结构

我们已经掌握了if语句:当条件满足的情况下,if语句后的语句执行,否则不执行,但是这个语句只会执行一次

然而我们发现生活中很多的实际的例子是:同一件事情我们需要完成很多次。

那我们怎么做呢?C语言中给我们引入了:while语句,可以实现循环

while (表达式)
    循环语句;
while循环的执行流程图

比如我们用while循环实现:在屏幕上打印1~10的数字。

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

②while循环中的break和continue

<1>break介绍

break在while循环中的作用:在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。

比如:

int main()
{
	int i = 1;
	while (i <= 10)
	{
		if (i == 5)
			break;  // 在循环中只要遇到break,就停止后期的所有的循环,直接终止循环
		printf("%d ", i);
		i = i + 1;
	}
	return 0;
}

终端的输出结果为1 2 3 4。

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

<2>continue介绍

continue在while循环中的作用:在循环中只要遇到continue,本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分,进行下一次循环的入口判断。

比如:

// 代码1
int main()
{
	int i = 1;
	while (i <= 10)
	{
		if (i == 5)
			continue;  // 在循环中只要遇到continue,本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分,进行下一次循环的入口判断
		printf("%d ", i);
		i = i + 1;
	}
	return 0;
}

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

代码1的终端输出结果为1 2 3 4 (死循环)。

代码2的终端输出结果为2 3 4 6 7 8 9 10 11。

Note:while循环中的continue是用于终止本次循环的,直接跳转到while语句的判断部分,进行下一次循环。

③练习

<1>下面代码是什么意思?

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

该代码为输入一个字符并将其输出,当遇到文件结束标志EOF时停止循环。

Note:

  • getchar()函数的返回类型是整型,返回值是输入字符的ASCII码值或者EOF(-1)。因此定义整型变量ch来接收输入字符的ASCII码值或者EOF(-1)。
  • putchar()函数将整数ch以对应ASCII码值的字符形式进行输出。
  • EOF(end of file)是文件结束标志,值为-1。getchar()函数读取失败的时候就会返回EOF,Windows操作系统下键盘输入ctrl+z时输入EOF。
  • scanf()函数和getchar()函数并不是直接从键盘上读取数据,实际上,键盘输入数据后,数据先存储在输入缓冲区中,然后scanf()函数和getchar()函数再从输入缓冲区中读取数据。因此,输入函数读取数据时,首先看输入缓冲区中有无数据,有数据直接读取数据,无数据等待键盘输入数据。

<2>下面代码是什么意思?

#include <stdio.h>
int main()
{
	int ch = 0;
	while ((ch = getchar()) != EOF)
	{
		if (ch < '0' || ch > '9')
			continue;
		putchar(ch);
	}
	return 0;
}

该代码为输入一个字符,输入字符为'0'~'9'时将该字符输出,输入字符为其他字符时则不输出,当遇到文件结束标志EOF时停止循环。

<3>基于以上两个问题的讲解,请设计一个输入密码并确认密码的使用场景。

有些同学可能会写出下面的错误代码:

#include <stdio.h>
int main() 
{
	printf("请输入密码:");
	char password[10] = { 0 };
	scanf("%s", password);
	printf("是否确认密码(Y/N):");
	int ret = getchar();
	if (ret == 'Y')
    {
		printf("确认成功\n");
	}
	else
    {
		printf("放弃确认\n");
	}
	return 0;
}

当输入密码摁下回车键后,程序还未等我们是否确认密码终端就已经输出放弃确认了。

这是因为输入的密码被password读取走后,输入缓冲区还剩下最后摁下的回车键(字符为'\n')。这会导致getchar()函数将直接从输入缓冲区接收字符'\n',并将'\n'的ASCII码值传给ret,由于与字符'Y'不相等从而放弃确认。

因此我们需要在是否确认密码前先清空输入缓冲区,但下面的代码为什么依旧是错误的:

#include <stdio.h>
int main()
{
	printf("请输入密码:");
	char password[10] = { 0 };
	scanf("%s", password);
	printf("是否确认密码(Y/N):");
	getchar();
	int ret = getchar();
	if (ret == 'Y')
    {
		printf("确认成功\n");
	}
	else
    {
		printf("放弃确认\n");
	}
	return 0;
}

当输入密码摁下回车键后,程序可以让我们是否确认密码了。这是因为getchar()函数将输入缓冲区里的残留字符'\n'读取清空了,输入缓冲区里没有数据,因此等待键盘输入数据来确认。

但是该代码错误的原因是,假如我们的密码为123456 abcd(有空格),我们发现程序又不能按照我们的预期结果运行了。

这是因为scanf()函数读取到空格就结束读取,输入缓冲区里还剩下 abcd,依旧不能是否确认密码,所以需要while循环将输入缓冲区全部读取清空。

下面为本题正确的代码:

#include <stdio.h>
int main()
{
	printf("请输入密码:");
	char password[10] = { 0 };
	scanf("%s", password);
	printf("是否确认密码(Y/N):");
	while (getchar() != '\n')
	{
		;
	}
	int ret = getchar();
	if (ret == 'Y')
	{
		printf("确认成功\n");
	}
	else
	{
		printf("取消确认\n");
	}
	return 0;
}

有些细心的同学可能会发现,实际上password存储的密码并不是123456 abcd,而是123456。

这是因为scanf()函数读取到空格就结束,至于想将密码存储空格并不是本章的重点,我们只需要掌握如何通过循环语句清空输入缓冲区就可以了。

2.for循环

①语法结构

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

表达式1初始化部分,用于初始化循环变量的。表达式2条件判断部分,用于判断循环时候终止。表达式3调整部分,用于循环条件的调整。

for循环的执行流程图

比如我们用for循环实现:在屏幕上打印1~10的数字。

int main()
{
    int i = 0;
    //for (i = 1/*初始化部分*/; i <= 10/*条件判断部分*/; i++/*调整部分*/)
    for (i = 1; i <= 10; i++)
    {
        printf("%d ", i);
    }
    return 0;
}

Question:在循环结构中,已经存在了while循环,为什么还要出现一个for循环呢?

Example:现在我们对比一下for循环和while循环的结构。

int main()
{
	int i = 0;
	
    // 实现相同的功能,使用while
	i = 1;  // 初始化部分
	while (i <= 10)  // 条件判断部分
	{
		printf("hehe\n");
		i = i + 1;  // 调整部分
	}
	
    //实现相同的功能,使用for
	for (i = 1; i <= 10; i++)  // 初始化部分,条件判断部分,调整部分
	{
		printf("hehe\n");
	}
	
	return 0;
}

通过上面代码我们可以发现,while循环和for循环都存在循环的三个必要条件,但是二者的代码风格有所不同。

Answer:while循环的三个部分很可能偏离较远,这样查找修改就不够集中和方便。for循环的风格更胜一筹,将三个影响循环的部分集中起来,更好处理。 for循环使用的频率也最高。

②for循环中的break和continue

在for循环中也存在break和continue,他们的意义和在while循环中是一样的。

但是在代码风格上还是有些差异,请见下面代码:

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

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

for循环不易出现死循环现象,这是因为for循环中的continue跳过continue后面的代码后,先调整循环变量,再进入条件判断部分,不容易造成死循环。

while循环易出现死循环现象,这是因为while循环中的continue跳过continue后面的代码后,有时并不调整循环变量,而是直接进入条件判断部分,容易造成死循环。

Note:在这里给大家介绍两个关于for循环的循环控制变量的编程的好习惯。

  • 不可在for循环体内修改循环变量,防止for循环失去控制。
  • 建议for语句的循环控制变量的取值采用“前闭后开区间”写法。
// 前闭后开的写法
for (i = 0; i < 10; i++)  // 在该写法中循环10次一目了然
{
	printf("%d ", i);
}

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

③一些for循环的变种

<1>for循环的三个部分的省略变种

比如:

int main()
{
	for (;;)
	{
		printf("hehe\n");
	}
	return 0;
}

实际上,初始化、条件判断、调整三个部分在for循环中都可以省略。其中for循环的条件判断部分如果被省略,则判断条件恒为真,死循环。

Note:for循环的三个部分的省略容易造成程序bug,因此如果不是非常熟练,建议不要省略。

比如下面两个代码:

// 代码1
int main()
{
    int i = 0;
    int j = 0;
    for (i = 0; i < 10; i++)
    {
        for (j = 0; j < 10; j++)
        {
            printf("hehe\n");
        }
    }
    return 0;
}

// 代码2
int main()
{
    int i = 0;
    int j = 0;
    for (; i < 10; i++)
    {
        for (; j < 10; j++)
        {
            printf("hehe\n");
        }
    }
    return 0;
}

代码1不必运行程序,观看代码就显然知道是在终端循环输出100次hehe。 

但是如果把代码1的初始化部分省略掉,写成代码2的形式,程序终端应该打印几次hehe呢?

对于使用省略不熟练的同学,可能依旧想当然地认为和代码1一样在终端循环输出100次hehe,但是实际上是终端循环输出10次hehe。

i=0进入外层循环,内层循环输出10次hehe,j=10内层循环结束;i=1进入外层循环,此时j=10并未初始化,不满足条件判断部分,终端不输出hehe;……;i=9进入外层循环,此时j=10并未初始化,不满足条件判断部分,终端不输出hehe。因此代码2终端总共输出10次hehe。

通过上述可知,省略前和省略后结果大不相同,因此不要随便省略。

<2>for循环的循环控制部分可用多个循环变量控制变种

比如:

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

在上面代码中,for循环的循环控制部分使用了x,y两个循环变量进行控制。

Note:for循环的循环控制部分也可以用两个及以上的循环变量来进行控制。

④练习

<1>阅读下面代码,请问循环要循环多少次?

#include <stdio.h>
int main()
{
    int i = 0;
    int k = 0;
    for (i = 0, k = 0; k = 0; i++, k++)
        k++;
    return 0;
}

答案是该代码循环0次。

for循环的条件判断部分(k=0)是将0赋值给k,0为假,不进入循环结构,循环0次;

若条件判断部分(k=0)改成(k==0),则循环1次。

3.do...while()循环

①语法结构

do
    循环语句;
while (表达式);
do...while()循环的执行流程图

比如我们用do...while()循环实现:在屏幕上打印1~10的数字。

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

Note:

  • while循环、for循环是先判断在执行;do...while()循环是先执行再判断。
  • while循环、for循环可能循环执行0次;do...while()循环至少执行一次。
  • do...while()循环使用的场景有限,不是经常使用。

②do...while()循环中的break和continue

在do...while()循环中也存在break和continue,他们的意义和在while循环中是一样的。

为了帮助大家体验使用场景,请大家阅读下面代码:

// 代码1(break使用场景)
int main()
{
    int i = 0;
    do
    {
        if (5 == i)
            break;
        printf("%d ", i);
        i++;
    } while (i < 10);
    return 0;
}

// 代码2(continue使用场景)
int main()
{
    int i = 0;
    do
    {
        if (5 == i)
            continue;
        printf("%d ", i);
        i++;
    } while (i < 10);
    return 0;
}

代码1的终端输出结果为0 1 2 3 4。

代码2的终端输出结果为0 1 2 3 4 (死循环)。

六、分支语句与循环语句总练习

<1>计算n的阶乘。

#include <stdio.h>
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = 1;
	int i = 0;
	for (i = 1; i <= n; ++i)
	{
		ret = ret * i;
	}
	printf("%d\n", ret);
	return 0;
}

<2>计算1!+2!+3!+……+n!。

#include <stdio.h>
int main()
{
	int n = 0;
	scanf("%d", &n);
	int sum = 0;
	int ret = 1;
	int i = 0;
	for (i = 1; i <= n; ++i)
	{
		ret = ret * i;
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;
}

<3>在一个有序数组中查找具体的某个数字x。

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };  // 有序数组
	int left = 0;
	int right = sizeof(arr) / sizeof(arr[0]) - 1;
	
	int x = 0;
	scanf("%d", &x);  // 输入欲查找的数字x
	int mid = 0;

	// 折半查找(二分查找)
	while (left <= right)
	{
		mid = (left + right) / 2;
		if (x < arr[mid])
		{
			right = mid - 1;
		}
		else if (x > arr[mid])
		{
			left = mid + 1;
		}
		else
		{
			break;
		}
	}
	
	if (left <= right)
	{
		printf("%d在该数组中的位序为%d\n", x, mid + 1);
	}
	else
	{
		printf("该数组中没有值为%d的数\n", x);
	}
	return 0;
}

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

#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
int main()
{
	char arr1[] = "For the ideal that I hold dear to my heart,I'd not regret a thousand times to die.";
	char arr2[] = ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<";
	int left = 0;
	int right = sizeof(arr2) / sizeof(arr2[0]) - 2;

	while (left <= right)
	{
		arr2[left] = arr1[left];
		arr2[right] = arr1[right];
		printf("%s\n", arr2);

		Sleep(300);  // 休眠操作,单位是ms,需包含<Windows.h>
		system("cls");  // 清屏操作,system是库函数,用于执行系统命令,需包含<stdlib.h>

		left++;
		right--;
	}
	printf("%s\n", arr2);
	return 0;
}

<5>编写代码,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成功,如果三次均输入错误则退出程序。)

#include <stdio.h>
#include <string.h>
int main()
{
	char password[10];
	int i = 1;
	while (i <= 3)
	{
		printf("请输入密码:");
		scanf("%s", password);
		if (strcmp(password, "123456") == 0)  // 两个字符串比较不能用(password == "123456")
		{
			break;
		}
		else
		{
			printf("密码输入错误,您还剩%d次输入密码的机会!\n", 3 - i);
			i++;
		}
	}
	if (i > 3)
	{
		printf("退出程序\n");
	}
	else
	{
		printf("登陆成功\n");
	}
	return 0;
}

<6>编写游戏,实现猜数字游戏。(计算机给一个1~100之间的随机数,用户在该范围内猜随机数为何值。)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
void menu()
{
	printf("##########################\n");
	printf("  Guess The Numbers Game  \n");
	printf("          1.play          \n");
	printf("          0.exit          \n");
	printf("##########################\n");
}

void game()
{
	int rand_num = rand() % 100 + 1;
	int guess_num = 0;
	while (1)
	{
		printf("请输入你要猜的数字:");
		scanf("%d", &guess_num);
		if (guess_num > rand_num)
		{
			printf("猜大了,请继续猜@。@\n\n");
		}
		else if (guess_num < rand_num)
		{
			printf("猜小了,请继续猜@。@\n\n");
		}
		else
		{
			printf("牛波一,你猜对了~。~\n\n");
			break;
		}
	}
}

int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择(1/0):");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("游戏开始\n");
			game();
			break;
		case 0:
			printf("游戏退出\n");
			break;
		default:
			printf("输入错误,请重新输入!\n");
			break;
		}
		Sleep(500);
		system("cls");
	} while (input);
	return 0;
}

七、goto语句

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

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

    for (...)
    {
        for (...)
        {
            for (...)
            {
                if (disaster)
                    goto error;
            }
        }
    }
error:
    if(disaster)
        // 处理错误情况

Note:

  • goto语句不能跨函数跳跃,只能在本函数内跳跃。
  • goto语句在代码里跳来跳去,易打乱代码流程,不推荐大家使用,了解其用法即可。

下面是使用goto语句的一个例子(一个关机程序):

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

而如果不使用goto语句,则可以使用循环:

#include <stdio.h>
#include <stdlib.h>
#include <string.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;
}
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少校0778

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值