C语言的分支和循环

C语言的分支和循环


封面

前言

C语言是结构化的程序设计语⾔,这⾥的结构指的是顺序结构、选择结构、循环结构,C语言是能够实现这三种结构的。如果我们仔细分析,我们日常所见的事情都可以拆分为这三种结构或者这三种结构的组合。
我们可以使⽤ifswitch实现分支结构,使⽤forwhiledo while实现循环结构。

分支结构

if 语句

if语句

if语句的语法规则:

if (表达式)
   语句

若表达式为真,则执行语句;若表达式为假,则不执行语句。
表达式的值为0,则不执行语句;表达式的值为非0,则执行语句。


大家可能经常看到if (a==1)这样的语句,这样的表达式是否还符合上面的规则呢?答案是肯定的。a==1是一个判断语句,如果a==1,则表达式返回值 1,如果a!=1,则表达式返回值 0。也就是说,判断语句的判断结果是通过返回值来反映的,返回值为1(真),则语句正确;返回值为0(假),则语句不正确。
所以这就引出了一个常见的错误,初学者可能会不小心写成if (a=1),仔细看看这两个语句有什么不同,这样写会导致什么后果?如果这样写的话,会导致a的值被赋为1,且语句必定执行。为什么呢?因为括号中是一个赋值语句,将a的值赋为1,然后if判断1为真,执行语句的。如果a被赋为非0的值,则语句一定会执行,如果a被赋为0,则语句一定不会执行,if语句就失去了选择分支的作用。


下面举一个简单的例子:输入一个整数,判断是否为奇数。

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

if语句如果不成立,可以继续判断多个分支条件,这时就需要用到elseelse if语句。
else if的使用规则和if相同,在前面的ifelse if都不成立的情况下会执行判断。
else后面不跟语句,在前面的ifelse if都不成立的情况下直接执行。
需要注意的是,C语言中的ifelse ifelse的后面都只能跟一个语句。

#include<stdio.h>
int main()
{
	int a;
	scanf("%d", &a);
	if (a % 2 == 0)
		printf("%d是偶数", a);//①
	    printf("------------");//②
	else
		printf("%d是奇数", a);//③
	    printf("************");//④
	return 0;
}

大家可能认为如果a是偶数,就会执行语句①②,但是这段代码是执行不了的,else找不到if,编译器会报错。因为if后面只能跟一个语句,所以判断完a是偶数,就执行语句①,然后见到了语句②,也会执行,此时的if语句就已经结束了,因为他在执行完语句①后没有遇到else ifelse,所以他就认为结束了,这时当程序执行到else时,就会提示缺少语句,在没有if的情况下使用了else


再来一个例子证明一下

int main()
{
	int a;
	scanf("%d", &a);
	if (a % 2 == 0)
		printf("%d是偶数\n", a);printf("——————\n");return 0;
}

运行结果
可以看到,在没有进入if语句的情况下,还是执行了语句②,所以if语句后面只能控制一条语句。


如果想让if语句后面可以跟多条语句的话,我们可以把多个语句塞进一个语句块内,把语句块作为if的执行语句,就可以实现执行多个语句的效果了(语句块就是用"{ }"把多个语句括起来)。同理,else if也是一样。

int main()
{
	int a;
	scanf("%d", &a);
	if (a % 2 == 0)
	{
		printf("%d是偶数\n", a);
		printf("——————\n");
	}
	else if (a % 2 != 0)
	{
		printf("%d是奇数\n", a);
		printf("************\n");
	}
	return 0;
}

运行结果


悬空 else 问题

如果有多个ifelse,可以记住这样⼀条规则,else总是跟最接近的if匹配,而与缩进无关。


int main()
{
	int a = 0;
	int b = 2;
	if(a == 1)
		if(b == 2)
			printf("执行语句一\n");
	else
		printf("执行语句二\n");
return 0;
}

大家可以猜一猜上面代码的运行结果,大家可能会认为,a != 0,应该执行语句二,但其实正确的结果是,语句一和语句二都不执行。
这是因为else总是跟最接近的if匹配,与缩进无关,所以在第一个if判断为假后,会直接跳到return 0


如果我们像让代码按照我们刚刚想的那样运行该怎么办呢?很简单,用{}。

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配对了。
所以在写代码时要多使用大括号,不仅可以减少bug的出现,也会使代码更清晰,便于调试。

关系运算符

C 语言用于比较的表达式,称为“关系表达式”,里面使用的运算符就称为“关系运算符”,主要有下面6个。
> < >= <= == !=分别对应大于、小于,大于等于、小于等于、等于、不等于。
关系表达式通常返回0或1,表示真假。
如果判断式成立,则为真,返回1;如果判断式不成立,则为假,返回0。
这样就导致关系判断式不能连写,如:i<j<k。这样的判断式是符合语法规则的,编译器不会报错,但是它的功能并不是我们想象的那样。
比如下面的例子:


int main()
{
	int age = 10;
	if(18<=age<=36)
	{
		printf("⻘年\n");
	}
	return 0;
}

10<18,大家可能会认为该程序什么都不会输出,但实际运行的话,该程序会执行输出语句。
因为在执行if (18<=age<=36)时,按照由左往右的顺序,先由10和18比较,18<=age为假,返回0,然后由0和36作比较,0<=36为真,返回1,则执行输出语句。

条件操作符

条件操作符也叫三目操作符,需要接受三个操作数,形式如下:

exp1 ? exp2 : exp3

条件操作符的计算逻辑是:如果exp1为真,exp2计算,计算的结果是整个表达式的结果;如果exp1为假,exp3计算,计算的结果是整个表达式的结果。
如下代码:

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d", &a);
	if (a > 5)
		b = 3;
	else
		b = -3;
	printf("%d\n", b);
	return 0;
}

如果使用条件操作符表示:

int main()
{
	int a = 0;
 	int b = 0;
 	scanf("%d", &a);
 	b = a>5 ? 3:-3;
 	printf("%d\n", b);
 	return 0;
}

例2:使用条件操作符找出两个数中的较大值

int main()
{
	int a = 0;
 	int b = 0;
 	scanf("%d %d", &a, &b);
 	int m = a>b ? a : b;
 	printf("%d\n", m);
 	return 0;
}

逻辑操作符

逻辑运算符提供逻辑判断功能,用于构建更复杂的表达式,主要有下面三个运算符。
! :逻辑取反运算符(改变单个表达式的真假)。
&& :与运算符,就是并且的意思(两侧的表达式都为真,则为真,否则为假)。
|| :或运算符,就是或者的意思(两侧至少有⼀个表达式为真,则为真,否则为假)。
这里的&和与门的简写是一样的,功能也是一样的,或门的功能在C语言中由||来表示,非门由!表示。
例:写一个判断是否是闰年的程序

//代码1
int main()
{
	int year = 0;
	scanf("%d", &year);
 	if(year%4==0 && year%100!=0)
 		printf("是闰年\n");
 	else if(year%400==0)
 		printf("是闰年\n");
 	return 0;
}

//代码2
int main()
{
	int year = 0;
 	scanf("%d", &year);
 	if((year%4==0 && year%100!=0) || (year%400==0))
 		printf("是闰年\n");
 	return 0;
} 
短路

C语言逻辑运算符还有⼀个特点,它总是先对左侧的表达式求值,再对右边的表达式求值,这个顺序是
保证的。如果左边的表达式满足逻辑运算符的条件,就不再对右边的表达式求值。这种情况称为“短路”。
“短路”会导致右边的表达式不执行,有时会对程序的结果产生影响,且这种bug不容易被发现,在使用&&||时,需小心短路现象。
例:请写出如下代码的运算结果

int main()
{
	int i = 0,a=0,b=2,c =3,d=4;
	i = a++ && ++b && d++;
	printf("i = %d\na = %d\nb = %d\nc = %d\nd = %d\n",i, a, b, c, d);
	return 0;
}

i,a,b,c,d的值分别为0,1,2,3,4.
因为在判断a++&&…时,a为0,则发生短路现象,后面的表达式不再计算,并将0赋给i,然后a+1。


int main()
{
	int i = 0,a=0,b=2,c =3,d=4;
	i = ++a && ++b && d++;
	printf("i = %d\na = %d\nb = %d\nc = %d\nd = %d\n",i, a, b, c, d);
	return 0;
}

i,a,b,c,d的值分别为0,1,2,3,5。分析同上。


int main()
{
	int i = 0,a=0,b=2,c =3,d=4;
	i = a++ || ++b || d++;
	printf("i = %d\na = %d\nb = %d\nc = %d\nd = %d\n",i, a, b, c, d);
	return 0;
}

i,a,b,c,d的值分别为1,1,3,3,4.
因为在判断a++&&++b时,b为3,则发生短路现象,后面的表达式不再计算,并将1赋给i,然后a+1。


int main()
{
	int i = 0,a=0,b=2,c =3,d=4;
	i = ++a || ++b || d++;
	printf("i = %d\na = %d\nb = %d\nc = %d\nd = %d\n",i, a, b, c, d);
	return 0;
}

i,a,b,c,d的值分别为1,1,2,3,4。分析同上。

switch 语句

switch语句是⼀种特殊形式的if...else结构,用于判断条件有多个结果的情况。它把多重的else if改成更易用、可读性更好的形式。
switch语句的语法规则

switch (expression) 
{
	case value1: statement
 	case value2: statement
 	default: statement
}

上面代码中,根据表达式expression不同的值,执行相应的case分支。如果找不到对应的值,就执行default分⽀。
需要注意的是:switch语句后面的表达式必须是整数类型,case后面的常量表达式也必须是整型常量表达式。同时,每一个case语句的最后应有break不然不会跳出seitch,而会继续运行下一个case

if 语句和 switch 语句的对比

理论上,switch语句能够完成的语句,if语句都能完成,只是在特定情况下,switch语句比if语句更简洁。
例:输入任意⼀个整数值,计算除3之后的余数。
如果使用if语句完成,如下:

int main()
{
	int n = 0;
 	scanf("%d", &n);
 	if(n%3 == 0)
 		printf("整除,余数为0\n");
 	else if(n%3 == 1)
 		printf("余数是1\n");
 	else
 		printf("余数是2\n");
 	return 0;
}

如果使用switch语句改写,就可以是这样的:

int main()
{
	int n = 0;
 	scanf("%d", &n);
 	switch(n%3)
	{
 		case 0:
 			printf("整除,余数为0\n"); 
 			break;
 		case 1:
 			printf("余数是1\n"); 
 			break;
 		case 2:
 			printf("余数是2\n"); 
 			break;
 	}
	return 0;
}

上述的代码中,我们要注意的点有:

  1. case和后边的数字之间必须有空格
  2. 每⼀个case语句中的代码执行完成后,需要加上break,才能跳出这个switch语句。
switch 语句中的 default

在使用switch语句的时候,我们经常可能遇到⼀种情况,比如switch后的表达式中的值无法匹配代码中的case语句的时候,这时候要不就不做处理,要不就得在switch语句中加入default子句。
switch语句中case字句和default子句没有顺序要求 ,只要顺序是满足实际需求的就可以。不过一般把default子句放在最后处理。

循环结构

while 语句

while语句的语法结构和if语句非常相似。
while语句的语法规则:

while(表达式)
  语句;

if语句一样,while后面也只能跟一个语句,如果想实现多行代码的循环,可以使用大括号。
while循环流程图
首先上来就是执行判断表达式,表达式的值为0,循环直接结束;表达式的值不为0,则执行循环语句,语句执行完后再继续判断,是否进行下⼀次判断。


例:输入⼀个正的整数,逆序打印这个整数的每⼀位
例如:输入:1234,输出:4 3 2 1
题目解析

  1. 要想得到n的最低位,可以使用n%10的运算,得到的余数就是最低位,如:1234%10得到4
  2. 要想去掉n的最低位,找出倒数第⼆位,则使用 n=n/10 就可以去掉最低位,如:n=1234/10得到123,123相较于1234就去掉了最低位,123%10就得到倒数第⼆位3。
  3. 循环1和2两个步骤,在n变成0之前,就能到所有的位。
int main()
{
	int n = 0;
 	scanf("%d", &n);
 	while(n)
	{
 		printf("%d ", n%10);
 		n /= 10;
 	}
 	return 0;
}

for 语句

for循环是三种循环中使⽤最多的,for循环的语法形式如下:

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

表达式1 用于循环变量的初始化
表达式2 用于循环结束条件的判断
表达式3 用于循环变量的调整
for循环流程图
首先执行表达式1初始化循环变量,接下来就是执行表达式2的判断部分,表达式2的结果如果==0,则循环结束;表达式2的结果如果!=0则执行循环语句,循环语句执行完后,再去执行表达式3,调整循环变量,然后再去表达式2的地方执行判断,表达式2的结果是否为0,决定循环是否继续。
整个循环的过程中,表达式1初始化部分只被执行1次,剩下的就是表达式2、循环语句、表达式3在循环。


例:在屏幕上打印1~100的值

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

do while 语句

do whlie语句的语法规则

do{
语句
}while(表达式);

do while循环中先执行图上的“语句”,执行完语句,再去执行“判断表达式”,判断表达式的结果是!=0,则继续循环,执行循环语句;判断表达式的结果==0,则循环结束。
所以在do while语句中循环体是至少执行⼀次的,这是do while循环比较特殊的地方。
注意do while循环中的while语句的最后有;


例:在屏幕上打印1~100的值

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

break 和 continue 语句

在循环执行的过程中,如果某些状况发生的时候,需要提前终止循环,这是非常常见的现象。C语言中提供了breakcontinue两个关键字,就是应用到循环中的。
break的用是用于永久的终止循环,只要break被执行,直接就会跳出循环,继续往后执行。
continue的作用是跳过本次循环continue后边的代码,在for循环和while循环中有所差异。

while 循环中的 break 和 continue
break

例:

int main()
{
	int i = 1;
 	while(i<=10)
 	{
 		if(i == 5)
 			break;//当i等于5后,就执⾏break,循环就终⽌了
 		printf("%d ", i);
 		i = i+1;
 	}
	return 0;
}

执行结果
运行结果
打印了1,2,3,4后,当i等于5的时候,循环在break的地方终止,不再打印,不再循环。所以break的作用就是永久的终止循环,只要break被执行,break外的第⼀层循环就终止了。
以后我们在循环中,想在某种条件下终止循环,则可以使用break来完成我们想要的效果。

continue

continue是继续的意思,在循环中的作用就是跳过本次循环中continue后边的代码,继续执行下⼀次循环的判断。
例:

 int i = 1;
 while(i<=10)
 {
 if(i == 5)
 continue;
 //当i等于5后,就执⾏continue,直接跳过continue的代码,去循环的判断的地⽅
 //因为这⾥跳过了i = i+1,所以i⼀直为5,程序陷⼊和死循环
 printf("%d ", i);
 i = i+1;
 }
 return 0;
}

到这里我们就能分析出来,continue可以帮助我们跳过某⼀次循环continue后边的代码,直接到循环的判断部分,进行下⼀次循环的判断,如果循环的调整是在continue后边的话,可能会造成死循环。

for 循环中的 break 和 continue
break

while循环中的break⼀样,for循环中的break也是用于终止循环的,不管循环还需要循环多少次,只要执行到了break,循环就彻底终止。
例:

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

执行结果
运行结果

continue

例:

int main()
{
	int i = 1;
	for(i=1;i<=10;i++)
	{
		if(i == 5)
			continue;
 		//当i等于5后,就执⾏continue,直接跳过continue的代码,去循环的判断的地⽅
	printf("%d ", i);
 	}
	return 0;
}

执行结果
运行结果
所以在for循环中continue的作用是跳过本次循环中continue后的代码,直接去到循环的调整部分。未来当某个条件发生的时候,本次循环无需再执行后续某些操作的时候,就可以使用continue来实现。

continue 在 while 和 for 循环中的区别

使用continue都可以使程序跳过本次循环,不同的是,在while语句中,回跳到判断语句,容易跳过自增而导致死循环,可以把自增条件放在continue前面来避免;而在for语句中,会跳到自增语句,然后再执行判断,避免了死循环。

do while 循环中的 break 和 continue

do while语句中的breakcontinue的作用和while循环中几乎⼀模⼀样。

循环的嵌套

whiledo whilefor这三种循环往往会嵌套在⼀起才能更好的解决问题,就是:循环嵌套。
例:找出100~200之间的素数,并打印在屏幕上。(素数⼜称质数,只能被1和本⾝整除的数字。)
题目解析:

  1. 要从100-200之间找出素数,首先得有100-200之间的数
  2. 假设要判断i是否为素数,需要拿(2,i-1)之间的数字去试除i,需要产生(2,i-1)之间的数字,也可以使用循环解决。
  3. 如果(2,i-1)之间有数字能整除i,则i不是素数,如果都不能整除,则i是素数。
int main()
{
	int i = 0;
 	//循环产⽣100~200的数字
 	for(i=100; i<=200; i++)
 	{
 		//判断i是否为素数
 		//循环产⽣2~i-1之间的数字
 		int j = 0;
 		int flag = 1;//假设i是素数
 		for(j=2; j<i; j++)
 		{
 			if(i % j == 0)
 			{
 				flag = 0;
 				break;
 			}
		}
 		if(flag == 1)
 		printf("%d ", i);
 	}
 	return 0;
}

这是最容易想到的做法,但其实该做法还可以进行一些小优化。

  1. 因为素数一定不可能是偶数,所以可以从101开始判断,且只判断奇数
  2. 如果在进行取余操作时,到(i的开方)没有出现余数等于0的情况,则不用判断后面的i了
    所以优化后上述代码为:
#include<math.h>
int main()
{
	int i = 0;
 	for(i=101; i<=200; i+=2)//只产生奇数
 	{
 		//判断i是否为素数
 		//循环产⽣ 3~根号i 之间的数字
 		int j = 0;
 		int flag = 1;//假设i是素数
 		for(j=3; j<sqrt(i); j++)
 		{
 			if(i % j == 0)
 			{
 				flag = 0;
 				break;
 			}
		}
 		if(flag == 1)
 		printf("%d ", i);
 	}
 	return 0;
}

goto 语句

C语言提供了⼀种非常特别的语法,就是goto语句和跳转标号,goto语句可以实现在同⼀个函数内跳转到设置好的标号处。
例:

int main()
{
	printf("执行语句一\n");
 	goto next;
 	printf("执行语句二\n");
 next:
 	printf("跳过了语句二的执行\n"); 
return 0;
}

执行结果
运行结果
goto语句如果使用的不当,就会导致在函数内部随意乱跳转,打乱程序的执行流程,所以建议是能不用尽量不去使用;但是goto语句也不是一无是处,在多层循环的代码中,如果想快速跳出使⽤goto就非常的方便了。

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

本来for循环想提前退出得使用break,⼀个break只能跳出⼀层for循环,如果3层循环嵌套就得使用3个break才能跳出循环,所以在这种情况下使用goto语句就会更加的快捷。

结语

分支(或者说选择)和循环语句在C语言中非常重要,只有多写才能检验自己的不足,我在写代码的时候经常会犯各种小错误,比如文中提到的do while语句中没加;while语句的判断条件写成赋值等等各种错误,这类错误一眼看上去很难看出来,需要取一句一句调试,所以尽量还是在写代码的时候就规避这些错误,养成良好的写代码习惯。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值