小言_互联网的博客

【C语言从青铜到王者】第一篇·详解分支与循环

500人阅读  评论(0)

本篇前言
前文我们说到,编程的一个目的就是借助计算机帮助我们解决生活中的问题,而世界运行的基本时间逻辑,在向计算机描述时可以大致归纳成三种逻辑结构:顺序、选择、循环。
顺序就是从前往后,从上到下,我们的写好的程序就是按顺序从第一行起被一行一行的编译的
选择也叫分支,是同一时间点面对问题时,有不同的选择,我们选其一而行
循环是指程序进入了一段不断重复的路,可能会一直在此循环往复,也可以打破循环,继续前进。
在顺序执行的程序中,分支中可能有循环,循环中也可能有分支。分支和循环是让我们的代码变得多姿多彩的关键技能,今天就让我们来升级它们吧!



分支语句


if - else语句

if-else语句就是由if、else(、else if)组成的语句块。它是分支语句的一种,也是我们写出选择结构必须掌握的语句
我在这里不厌其烦的把if-else语句分成了三种:单if语句、if-else双分支语句、if-else if-else多分支语句


单if语句

单if语句就是没有else只有if的语句。

if(条件表达式)
{
执行的语句;
}

if的英文意思是“如果”,我们可以按照语义理解成“如果我满足条件就执行语句”。单if语句完成的就是这么一个功能。那如果不满足条件呢?不满足条件,就不执行语句。这里可以看出if-else不是一定得配套使用,只要有if就可以做出最基本的判断,这个特征在if-else if-else语句中同样适用,所以以后见到只有if或只有if-else if的语句就不要觉得奇怪了。
给出样例代码:

#include<stdio.h>
int main()
{
   
	int age = 20;
	if (age > 18)
	    printf("可以考驾照\n");
	return 0;
}


代码分析:由于age=20,20>18,if后()里的条件判断为真,所以执行语句printf(“可以考驾照\n”);


知识补充一

往后讲之前我们先补充两个基础知识:
给出样例代码:

#include<stdio.h>
int main()
{
   
	int a = 0;
	scanf("%d", &a);
	if (a < 60)
	{
   
		printf("挂科\n");
	}
	else
	{
   
		printf("稳过\n");
	}
	return 0;
}


1.C语言的真和假与if语句判断的条件
C语言中的“真”和“假”可以简单的理解成“非零”和“零”。选择语句其实就是通过判断条件的真假来“做出选择”,这个条件就是写在if后面的()里的表达式。上面的程序中,这个条件就是“a<60”,前面我们说过“<”的意思是“判断是否小于”,结果为真就输出1,结果为假就输出0。所以“a<60”的意思就是判断a是否小于60,我们通过scanf键入的a的值是60,所以判断为假,表达式的结果是0,为假,执行else语句,屏幕打印出“稳过”。
(博主希望大家在都能稳过( ̄▽ ̄))

2.if后的执行语句加不加大括号的问题
通过下面的程序来看不加大括号的情况。不加大括号时,if条件判断为真时会默认只执行其后最近的一条语句

#include<stdio.h>
int main()
{
   
	int age = 14;
	if (age < 18)
		printf("未成年\n");
	return 0;
}


通过下面的程序看一下加大括号的情况。加大括号时,if条件判断为真时会执行大括号内所有语句

#include<stdio.h>
int main()
{
   
	int age = 14;
	if (age < 18)
	{
   
		printf("未成年\n");
		printf("不能谈恋爱\n");
	}
	return 0;
}


再看下面这段程序,我们的age明明已经到18了却仍然不能谈恋爱,这是为什么呢?原因就是“不加大括号时if仅与其后最近的一条语句配对”,由于age是18,条件判断为假,所以不执行printf(“未成年\n”);然后不论条件真假,程序都会按照顺序执行printf(“不能谈恋爱\n”);

#include<stdio.h>
int main()
{
   
	int age = 18;
	if (age < 18)
		printf("未成年\n");
		printf("不能谈恋爱\n");
	return 0;
}

知识总结:
1.C语言中if后的条件判断:非零为真,零为假
2.不加大括号时if仅与其后最近的一条语句配对


if - else双分支语句

双分支语句其实就是判断题,非if即else
给出语法:

if(条件表达式)
{
执行的语句1;
}
else
{
执行的语句2;
}

条件表达式为真,执行if后语句,条件表达式为假,执行else后语句
案例:

#include<stdio.h>
int main()
{
   
	int age = 0;
	scanf("%d", &age);
	if (age < 18)
		printf("未成年\n");
	else
		printf("成年\n");
	return 0;
}



if - else if - else多分支语句

多分支语句就是将判断题变成了多选题
给出语法:

if(条件表达式1)
{
执行的语句1;
}
else if(条件表达式2)
{
执行的语句2;
}

else if(条件表达式n)
{
执行的语句n;
}
else
{
执行的语句n+1;
}

条件表达式1为真则执行语句1,条件表达式2为真则执行语句2,以此类推,条件表达式n为真,执行语句n; 如果,以上的n个条件都不满足,执行else下的语句n+1。
案例:

int main()
{
   
	int score = 80;
	if (score >= 0 && score < 60)
		printf("挂科\n");
	else if (score >= 60 && score < 70)
		printf("中等\n");
	else if (score >= 70 && score <= 90)
		printf("良好\n");
	else if (score >= 90 && score <= 100)
		printf("优秀\n");
	else
		printf("成绩出错\n");
}




小问题:每个条件表达式的分数段的下限一定需要写出来吗?答案是不需要,因为当前一个条件判断不过时,已经自动的确认了后一个条件判断的下限。

常见错误提醒一

初学者可能会把这条语句

if (score >= 0 && score < 60)

写成

if (0 <= score < 60)

如果我们这么写,我们来测试一下程序的结果是什么(我们假设成绩是80)

明明是80分怎么会挂科呢?是改卷老师心情不好吗?并不是,其实这条语句

if (0 <= score < 60)

的逻辑是:先执行“0 <= score”,score为80,所以表达式“0 <= score”为真,表达式的结果就是1,然后“1 < 60”这个表达式也是真,所以if后()内的表达式值为1,直接打印了“挂科”。由此启示我们,不要想当然的把数学中的表达式写在程序中,C语言有自己的语法,我们要严格的遵守它才能让它为我们所用。


悬空else的匹配问题

看这段代码,先自行计算结果

int main()
{
   
	int a = 10;
	int b = 20;
	if (a == 20)
		if (b == 20)
			printf("%d", b);
	else
		printf("%d", a);
    return 0;
}

结果是什么呢?
结果是什么都不打印。原因是else默认与离自己最近的未匹配的if匹配。上面的程序书写风格不好,在我们的编程软件(Visual Studio 2019)上这段代码实际上会被自动的排版成下面这种形式。

int main()
{
   
	int a = 10;
	int b = 20;
	if (a == 20)
		if (b == 20)
			printf("%d", b);
		else
			printf("%d", a);
	return 0;
}

小伙伴们一定要学习养成好的代码风格!当然,我们也应该具备识别书写不规范代码的能力

常见错误提醒二

初学者可能会把这句

if (a == 20)

写成

if (a = 20)

那么这个表达式就不再是一个判断的表达式,而是一个赋值的表达式,表达式的结果就是a的值,为20,非零,为真,程序会直接执行if的语句,而且更糟糕的是,我们如果以后在很长的程序中不小心犯了这样的错误,编译器还不会报错,但是程序的结果肯定是不对的,那可就真是“死都不知道怎么死的”了。
为了杜绝这种错误,我们学习一个小技巧
小技巧:
上面的语句可以写成

if (20 == a)

这样的话如果我们哪天丢掉了一个=,写成了

if (20 = a)

程序就会直接报错


switch语句


初识switch语句

switch语句是一种适用于处理多分支的语句
给出语法:

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

switch后面的()内是整形表达式,就是说这个表达式结果的数据类型必须是整形(实际上字符型char也可以,以后遇到再细说) case后面的表达式是整形常量表达式,就是说这个表达式不仅结果必须是整形,而且结果必须是常量

这个语法是什么意思呢?我们这样理解:switch的英文意思是开关,那么switch后面整形表达式的值代表的就是这个开关在哪里被打开,也就是说这个整形表达式的值决定了整个switch程序块的入口

比如下面的代码:

int main()
{
   
	int day = 3;
	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;
}


从上面的程序中就可以看出,程序是从上往下的,从这个入口(day = 3,也就是case 3)开始,一行一行的执行。但是光是这样,switch是做不到分支的功能的,所以我们还需要一个出口


必不可少的的break关键字

break充当的是switch程序块出口的角色。break是一个关键字,作用是让程序读到它时跳出自己当前所在的子程序。这样,程序块就被分划成了不同的部分,我们才能实现分支的功能

int main()
{
   
	int day = 6;
	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;
}


比如这个程序就从case 6入口进入,然后读了一句printf(“休息\n”),再读到break出口出选择程序块

知识补充二

swtich语句块的最后一项不管加不加break都可以跳出程序,但是编程的好习惯是加上,这样便于以后别人或我们修改程序增添case


常用的default子句

default子句和if-else中的else作用类似,都是将前面所有情况的补集作为自己的触发条件,也就是说前面的所有情况的条件判断都为假就执行它,而且它和else一样,并不是必须得存在。由于是所有情况的补集,所以default和else都至多只能出现一次
给出案例:

int main()
{
   
	int day = 8;
	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;
	default:
		printf("输入错误\n");
		break;
	}
	return 0;
}

由于8不在任何一个case里,所以它在前面所有情况的子集:default里,所以执行default子句内容

知识补充三

else和default有一个很大的区别就是它们在程序中的位置问题:
default可以在任意位置,而else只能在最后
比如上面的程序写成

int main()
{
   
	int day = 8;
	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;
	default:
		printf("输入错误\n");
		break;
	case 6:
		printf("休息\n");
		break;
	case 7:
		printf("休息\n");
		break;
	}
	return 0;
}

一点问题没有,程序照常执行
但是如果else放在了else if的前面,程序就会报错
这是因为switch实际上是把所有的情况看成了一块大饼切成不同的块块儿,块块儿的名字无所谓,反正都是块块儿。而if-else语句是有逻辑结构的,如果else放在了前面就和if直接配对形成程序块,后面的else if就无家可归了。


分支语句的嵌套

分支语句也就是if语句和switch语句,都是可以嵌套的。所谓分支嵌套就是在分支中再加入分支,这样我们就可以描述相对比较复杂的情况了

if嵌套
int main()
{
   
	int a = 1;
	int b = 0;
	if (a == 1)
	{
   
		printf("考入名校\n");
		printf("你是否要努力学习?(1/0):");
		scanf("%d", &b);
		if (b == 1)
			printf("成为大神\n");
		else
			printf("放飞自我,毕业即失业\n");
	}
	return 0;
}

switch嵌套
int main()
{
   
	int day = 8;
	switch (day)
	{
   
	case 1:
		printf("工作\n");
		break;
	case 2:
		printf("工作\n");
		break;
	case 3:
		printf("工作\n");
		int a = 1;
		switch (a)
		{
   
		case 1:
			printf("划水\n");
			break;
		case2:
			printf("努力工作\n");
			break;
		}
		break;
	case 4:
		printf("工作\n");
		break;
	case 5:
		printf("工作\n");
		break;
	default:
		printf("输入错误\n");
		break;
	case 6:
		printf("休息\n");
		break;
	case 7:
		printf("休息\n");
		break;
	}
	return 0;
}



循环语句

循环语句就是一些语句重复执行多次。C语言中的循环结构有三种常见的语句,分别是while、for、do-while循环,它们大同小异,都具有初始化部分条件判断部分调整部分,我们抓住这三个点来学习他们吧。


while循环

给出语法:

while(表达式)
{
循环语句;
}
其他语句;

表达式的值为真(非零),就重复执行循环语句,直到表达式的值为假(零),跳出循环,执行其他语句
案例“打印1-10”:

#include<stdio.h>
int main()
{
   
	int i = 1;
	while (i <= 10)
	{
   
		printf("%d ", i);
		i++;
	}
	return 0;
}

初始化部分:

int i = 1;

初始化部分的作用就是初始化一个循环变量

条件判断部分:

 (i <= 10)

条件判断部分就是我们初始化的这个循环变量的作用所在。循环变量的值的改变会导致条件判断表达式的值改变,从而决定循环是否继续。条件判断为真,就继续循环,条件判断为假,就跳出循环

调整部分:

i++;

调整部分就是让我们的循环变量的值发生改变的语句,这样我们的条件判断表达式的值才有可能改变,不会进入死循环(停不下来的循环就是死循环)
(这里的i++是i=i+1的意思,前文讲过,这里仍然提示一下)
循环体

printf("%d ", i);

循环体就是每次循环都会执行的循环语句,是我们循环语句的主体内容


for循环

清楚了while循环,后面两个就可以类比学习了
给出语法:

for(初始化部分;条件判断部分;调整部分)
{
循环语句;
}

对比于while比较分散的三部分,for就集中的多,直接将三个部分写在了for后面的括号中,清晰明了
同样给出“打印1-10”的案例方便同学们类比学习:

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


for循环还有几个注意点:

  • 1.一般不建议在for循环体中修改循环变量,可能会导致程序错乱
  • 2.for循环的三个部分可以都为空,直接用来表示死循环
	for (;;)
	{
   
		printf("阿巴");
	}


(光标一直在闪烁,说明程序一直在运行)

  • for循环可以同时使用多个循环变量
#include<stdio.h>
int main()
{
   
	int x, y;
	for (x = 0, y = 0; x < 2 && y < 5; x++, y += 2)
	{
   
		printf("%d%d", x, y);
	}
	return 0;
}

do-while循环

给出语法:

do
{
循环语句
} while(条件判断表达式);

程序逻辑:先执行一次循环语句,再进行条件判断是否继续循环。与for和while循环的区别是至少可以循环一次。
给出类比案例:

#include<stdio.h>
int main()
{
   
	int i = 1;
	do
	{
   
		printf("%d ", i);
		i++;
	} while (i <= 10);
	return 0;
}


do-while循环使用场景有限。总体的使用频率来说:for循环 > while循环 > do while循环


循环语句中的break和continue

  • break
    当程序读到break关键字的时候,直接跳出循环
    举例:
#include<stdio.h>
int main()
{
   
	int i = 0;
	for (i = 1; i <= 10; i++)
	{
   
		if (i == 5)
			break;
		printf("%d ", i);
	}
	return 0;
}


当i累加到5的时候,执行break,直接跳出循环,程序结束

  • continue
    当程序读到continue关键字的时候,跳过本次循环中continue以后的循环语句
    for循环中continue举例:
#include<stdio.h>
int main()
{
   
	int i = 0;
	for (i = 1; i <= 10; i++)
	{
   
		if (i == 5)
			continue;
		printf("%d ", i);
	}
	return 0;
}


当 i 累加到5的时候,执行continue,跳过本次循环剩下的语句printf("%d ", i),直接跳转到调整语句i++,然后进入下一次的条件判断语句,开始下一轮循环
while循环中continue举例:

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


程序结果是1 2 3 4 然后死循环(光标一直在闪烁说明程序一直未结束),这是因为i累加到5时,执行continue语句,跳过本次循环剩下的语句printf("%d “, i)和i++,直接跳转到条件判断 i <= 10,判断i=5 , 5<=10,然后又跳过本次循环剩下的语句printf(”%d ", i)和i++,直接跳转到条件判断 i <= 10,判断i=5 , 5<=10…
总结:
for循环中,由于调整部分不在循环体中,所以不会因为continue的影响陷入死循环;while循环中,调整部分在循环体中,极有可能因为continue陷入死循环


循环语句的嵌套

循环语句的嵌套就是在循环体中再加入另一个循环,从而会让我们的程序可以描述更多的循环情况,也提升了程序的空间复杂度
给出for嵌套案例:

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


打印九九乘法口诀表:

#include<stdio.h>
int main()
{
   
	int arr[] = {
    1,2,3,4,5,6,7,8,9 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 9; i++)
	{
   
		for (j = 0; j <= i; j++)
		{
   
			printf("%d × %d = %d ", arr[i], arr[j], arr[i] * arr[j]);
		}
		printf("\n");
	}
	return 0;
}



goto语句


goto语句的使用方法

goto语句相当于一个传送门,可以直接暴力的改变代码执行的逻辑,它就像一把双刃剑,用得好确实有奇效,但滥用可能会自废武功

给出语法:

goto 标记名称;

标记名称:其他语句;

用一个简单的例子认识一下使用方法

#include<stdio.h>
int main()
{
   
	int age = 0;
again:
	scanf("%d", &age);
	if (age < 18 && age >= 1)
		printf("未成年\n");
	else if (age >= 18 && age <= 121)
		printf("成年\n");
	else
	{
   
		printf("输入错误,请重新输入\n");
		goto again;
	}
	return 0;
}


我们输入200的时候,执行else里的语句,先执行printf(“输入错误,请重新输入\n”)然后执行goto again,again在程序最开始,所以我们可以继续进行选择判断。


goto语句的使用场景

goto是一把双刃剑,我们需要学会使用它的场景
使用goto的一个非常合适的场景就是用于跳出多重循环
看下面这个案例:

#include<stdio.h>
int main()
{
   
    int x, y, z;
    for (x = 0; x <= 100 / 5; x++)
        for (y = 0; y <= 100 / 3; y++)
            for (z = 0; z <= 100 * 3; z++)
            {
   
                if ((x * 5 + y * 3 + z / 3 == 100) && (z % 3 == 0) && (x + y + z == 100))
                {
   
                    printf("x=%d y=%d z=%d\n", x, y, z);
                }
                else
                    goto error;
            }
error:
    return 0;
}

这里的goto语句就像是一个保险,把深陷“多重梦境”中的程序一语点醒


至此为止,第一篇的内容结束。博主才疏学浅,又讲解不周的地方希望大家积极指出,我们共同探讨,共同进步。


转载:https://blog.csdn.net/qq_51379868/article/details/115690670
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场