分支和循环(一)

分支和循环(一)

C语言是结构化的程序设计语言

顺序结构

选择结构 if switch

循环结构 for while do while

if 语句

语法结构:
if(表达式)
语句 ;
if(表达式) 语句1;
else
    语句2;
//多分支
if(表达式1)
语句1;
else if(表达式2)
语句2;
else
语句3;

注意:多分支语句格式:if else if else if… else

若if、else后面要跟多条语句,需要加大括号{},一对大括号就是一个代码块

c语言的真假:0表示假,非0表示真 (或者用布尔类型)

悬空else

试想,下面一段代码的输出结果是什么呢

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

当我们看一眼这个代码时,觉得输出的是haha,其实不是这样的,因为C语言中对齐与否不代表配对关系,即第一个if和else对齐但不配对,与else配对的是最接近else的一个if,所以这段代码稍作修改如下:

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

那么这里很明显,a=0不满足第一个if的条件,程序运行根本不进入if语句中,因此这段代码什么都不输出

到这里,我们试着写一个简单的程序:输出1-100之间的奇数

#include
int main()
{
    int i=1;
    while(i<=100)
    {
        if(i%2==1)
            printf("%d",i);
        i++;
    }
    return 0;
}

switch语句

switch语句也是一种分支语句,常常运用到多分支的情况

下面是根据输入1-7显示星期的程序

#include
int main()
{
    if (1 == day)
		printf("星期1\n");
	else if (2 == day)
		printf("星期2\n");
	else if (3 == day)
		printf("星期3\n");
	else if (4 == day)
		printf("星期4\n");
	else if (5 == day)
		printf("星期5\n");
	else if (6 == day)
		printf("星期6\n");
	else if (7 == day)
		printf("星期天\n");
    
    return 0;
}

其实这段代码还有另一种表达方式

#include
int main(){
    int day=0;
    scanf("%d",&day);
    switch(day)
    {
	case 1:  //case 后必须是整型常量表达式
		printf("星期1\n");
		break;
	case 2:
		printf("星期2\n");
		break;
	case 3:
		printf("星期3\n");
		break;
	case 4:
		printf("星期4\n");
		break;
	case 5:
		printf("星期5\n");
		break;
	case 6:
		printf("星期6\n");
		break;
	case 7:
		printf("星期天\n");
		break;
    }
    return 0;
}

对switch语句的理解:case是各种情况的入口,当执行完后跳出(break)switch语句,break的作用即为分支

而switch后的值,必须是整型常量表达式

现在我们对这个程序要求稍作修改:当输入1-5时打印工作日,当6-7时打印休息日

对上述程序简单修改如下:

#include
int main(){
    int day=0;
    scanf("%d",&day);
    switch(day)
    {
	case 1:  //case 后必须是整型常量表达式
		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)1-5都对应相同的结果,而情况(case)6-7对应一种结果,所以面对上述冗余的代码,我们可以尝试对其进行改进,结果如下:

#include
int main(){
    int day=0;
    scanf("%d",&day);
    switch (day)//整型表达式
	{
	case 1:  //case 后必须是整型常量表达式
	case 2:
	case 3:
	case 4:
	case 5:
		printf("工作日\n");
		break;
        case 6:
	case 7:
		printf("休息日\n");
		break;
	default:
		printf("输入错误\n");
		break;
	}
}

因此,case后的break需不需要加,取决于你的逻辑目的。为了解决当输入的数字不是1-7时,我们使用default来解决不匹配的问题。

书写时,不要忘记case后加冒号

另外,switch语句允许嵌套使用,例:

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

循环语句

while循环

关于contine:continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分。进行下一次循环的入口判断。

而break的作用是直接跳过后面的所有循环

几个例子

#include 
int main()
{
	int ch = 0;
	while ((ch = getchar()) != EOF)
		putchar(ch);
	return 0;
}
//这里的代码适当的修改是可以用来清理缓冲区的 

getchar 首先,getchar是一个函数,int getchar(void),它的返回值为ASCII码,所有ASCII码表里有的字符它都能读取出来,因此getchar这个函数的返回类型是int型,而当读取错误时(ch = getchar()) == EOF),EOF返回值为-1。

我们来分析下面这个程序:

#include
int main()
{
    char password[20]={0};           //初始化数组
    printf("请输入密码:");
    scanf("%s",password);            //password是数组名不用取地址
    printf("请确认密码(Y/N):");
    int ch=getchar();                //ch存的是用户输入的字符的ASCII码
    if(ch=='Y'){
        printf("确认成功");
    }
	else {
        printf("确认失败");
    }
	return 0;
}

程序运行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ulAG2I9-1662121651971)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830170932774.png)]

我们会发现,当输入密码之后,还没有输入Y/N,就打印了确认失败

问题在于我们用scanf读取数据时,并不是键盘输入即被scanf给读取,而是暂存到输入缓冲区中

scanf——————————> abcccc\n 键盘

​ |

​ 输入缓冲区(\n代表回车)

现在scanf检测到输入缓冲区里有内容,就将它们读取走 ,而读取的是abcccc,放入password中储存

此时输入缓冲区只剩 \n

getchar读取时也遵循此原理,此时getchar读取到缓冲区有\n,储存到ch中,并继续判断是否为’Y’

改进方法:提前将输入缓冲区清理:

#include
int main()
{
    char password[20]={0};           //初始化数组
    printf("请输入密码:");
    scanf("%s",password);            //password是数组名不用取地址
    //清理\n
    getchar();                       //用getchar()函数将\n读取后不返回
    
    printf("请确认密码(Y/N):");
    int ch=getchar();                //ch存的是用户输入的字符的ASCII码
    if(ch=='Y'){
        printf("确认成功");
    }
	else {
        printf("确认失败");
    }
	return 0;
}

程序执行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-meLqw0Zw-1662121651972)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830172312503.png)]

那么现在,是否已经解决问题了呢?我们换一种输入情况验证,结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SWba6cnQ-1662121651972)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830172425114.png)]

这里我们在输入时加入了空格,程序运行仍然有问题

这里的问题在于,scanf遇到空格时会停止读取,因此这是输入缓冲区还剩cccc\n,而getchar()没有将输入缓冲区全部清理干净,因此出现了上述情况,这时我们不妨设计一个能完全清理掉输入缓冲区的程序:

#include
int main()
{
    char password[20]={0};           //初始化数组
    printf("请输入密码:");
    scanf("%s",password);            //password是数组名不用取地址
    //完全清理输入缓冲区
    int tmp=0;
    while((tmp=getchar())!='\n')
    {
        ;                           //一直读取字符直到读取到\n(清除)
    }
   
    printf("请确认密码(Y/N):");
    int ch=getchar();                //ch存的是用户输入的字符的ASCII码
    if(ch=='Y'){
        printf("确认成功");
    }
	else {
        printf("确认失败");
    }
	return 0;
}

此时程序执行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NR0GkPGs-1662121651973)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830173332190.png)]

下面我们来看另一个例子:

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

该段代码的作用是:只打印数字字符,跳过其他字符

for循环

for(表达式1;表达式2;表达式3){

​ 循环体;

}

例:

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

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

执行的顺序是:表达式1------>表达式2------>循环体------>表达式3

那么for循环同样的有break和continue的用法,与while循环的区别是,for循环接continue不会像while循环一样可能会出现死循环因为continue跳过后面的循环体,并不会跳过“i++”(调整部分)

综上,for循环比while循环更好用

一些建议:不可在for 循环体内修改循环变量,防止 for 循环失去控制;for语句的循环控制变量的取值采用“前闭后开区间”写法。

注意:

1.for的初始化、判断、调整三个部分都可以省略**(一旦省略判断部分,那么就意味着判断恒为真)**

//下面列举一个死循环的情况
#include
int main(){
    for(;;){
        printf("只有坏掉的东西才会停下来,人不会坏,也不能停");
    }
	return 0;
}

显然,输出的结果是一个死循环:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3iYcich1-1662121651973)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830175933144.png)]

2.循环的嵌套

#include
int main(){
    int i=0;
    int j=0;
        for(;j<10;j++){
            for(;i<10;i++){
                printf("悟已往之不谏,知来者之可追");
            }
        }
	return 0;
}

这里大家一眼直观认为,该循环会循环100次,那么结果真的是这样吗?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sdMZ8iuD-1662121651973)(C:\Users\kevin\AppData\Roaming\Typora\typora-user-images\image-20220830180605598.png)]

显然,该循环只循环了10次

分析:当i=0时,内层循环开始j从0加到9,当j=10时跳出内层循环,i++得1而再次进入内层循环后j的值仍等于10内层循环不执行,这里我们省略了初始化的部分才导致这样的结果,修改后如下:

#include
int main(){
    int i,j=0;
    for(j=0;j<10;j++){
    	for(i=0;i<10;i++){                 //初始化不能随便省略
        	printf("悟已往之不谏,知来者之可追");
        }
    }
	return 0;
}

printf(“悟已往之不谏,知来者之可追”);
}
}
return 0;
}


这里大家一眼直观认为,该循环会循环100次,那么结果真的是这样吗?

[外链图片转存中...(img-sdMZ8iuD-1662121651973)]

显然,该循环只循环了**10次**

分析:当i=0时,内层循环开始j从0加到9,当j=10时跳出内层循环,i++得1而再次进入内层循环后j的值仍等于10内层循环不执行,这里我们**省略了初始化的部分**才导致这样的结果,修改后如下:

```c
#include
int main(){
    int i,j=0;
    for(j=0;j<10;j++){
    	for(i=0;i<10;i++){                 //初始化不能随便省略
        	printf("悟已往之不谏,知来者之可追");
        }
    }
	return 0;
}

你可能感兴趣的:(c++,c语言,开发语言)