分支与循环结构

  c语言是结构化的程序设计语言。

  在实际生活中一般就有三种情况顺序、选择、循环,C语言是为了解决生活中的问题,当然在其设计时支持顺序结构、循环结构和选择结构。在本篇文章中主要是对这三种结构在C语言中的具体使用来进行说明。

目录

一、什么是语句?

二、分支语句(选择语句)

2.1   if语句

2.1.1悬空else(if与else的匹配问题)

2.1.2if书写形式的对比

2.1.3练习题

2.2 switch语句

2.2.1在switch语句中的break

2.2.2default子句

2.2.3练习

三、循环语句

3.1while循环

3.1.1while语句中的break和continue

3.1.3scanf即getchar读取相关问题

3.2 for循环

3.2.1 for循环的语法

3.2.2 break和continue在for循环中

3.2.3 一些for循环的变种

 3.2.4 一道小题

3.3 do......while()循环

3.3.1do......while 循环的语法

3.3.2 特点

3.3.3 do   while循环中的break和continue

 四、goto语句

一个关机程序


在c语言中使用一些语句来表达一些结构;

分支语句(选择结构):

  • if
  • switch

循环语句(循环结构):

  • while 
  • for
  • do   while

一、什么是语句?

c语言可以将语句分为以下5类:

  1. 表达式语句      例如2+3;
  2. 函数调用语句      例如Add(a,b);
  3. 控制语句
  4. 复合语句
  5. 空语句     仅有一个;,一个;就代表一个空语句。

在下文我们主要介绍的是控制语句

控制语句用于控制程序的执行流程以实现程序的各种结构方式(c语言支持三种结构:顺序结构、选择结构、循环结构),他们由特定的语句定义符组成,c语言有9种控制语句。

可以分为以下3类:

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

二、分支语句(选择语句)

2.1   if语句

那么if语句的语法是什么呢?

语法结构:

if(表达式)    //表达式的结果为真,语句执行     在c语言中0表示假,非0为真 

    语句;    //表达式为假,语句不执行

if语句有单分支、双分支、以及多分支类型:

具体举例为:

//单分支
#include 
int main()
{
	int age = 0;
	scanf("%d", &age);
	if (age < 18)
	{
		printf("未成年\n");
	}
	return 0;
}
//双分支
#include 
int main()
{
	int age = 0;
	scanf("%d", &age);
	if (age < 18)                    //如果age<18,则打印未成年
	{
		printf("未成年\n");
	}
	else                            //否则,打印成年
	{
		printf("成年\n");
	}
	return 0;
}
//多分支
#include 
int main()
{
	int age = 0;
	scanf("%d", &age);
	if (age < 18)                    //如果age<18,则打印未成年
	{
		printf("未成年\n");
	}
	else if(age >= 18 && age < 30)                            //,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;
}

在这里我们需要注意的是:在if或者else后面都是默认只能控制一条语句的,若其后要执行多条语句,就应该加{}(即代码块,一对{}就是一个代码块)。

#include 
int main()
{
	if (表达式)
	{
		语句列表1;   //多条语句
	}
	else (表达式)
	{
		语句列表2;   //多条语句
	}
	return 0;
}

2.1.1悬空else(if与else的匹配问题)

else的匹配:else是和他最近的if匹配的。

例如我们写一个代码

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

或许许多初学者会认为运行结果是haha但实际是不输出。认为输出haha的同学会把第一个if与else匹配,这样是不对的,if与else语句的配对遵守的是就近匹配原则。

解析:

在上述代码中a=0,此时不满足a==1就不会进入if语句中,那么在if语句中嵌套的if与else语句也不会执行。

在这里我们想是否可以通过一些{}使代码的逻辑更加清楚呢,增加代码的可读性,也就是说形成好的代码风格。

上面的代码使用好的代码风格改进之后就变成了,可读性增强

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

		}
		else
		{
			printf("haha\n");

		}
	}
		return 0;
}

2.1.2if书写形式的对比

1.

//代码一
if (condition) {
	return x;
}
return y;
//此代码含义为如果if后面的条件满足返回x否则返回y
//注意return只要返回了,后面的return就无法执行了
//代码二
if (condition) 
{
		return x;
}
else
{
	return y;
}

代码一与代码二运行结果一致,且算法思想一致但是对比之下我们发现代码2的可读性高,且代码风格好。

2.

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

代码三是一个很简单的代码,但是如果我们粗心将表达式(if(表达式))中num==5误写成了num=5时,代码不会报错,但是会导致代码运行结果与预期结果不符。如果在一个大的工程中将num==5写为了num=5,编译器不会报错,我们也无法及时知道代码的出错点,会降低时间效率。

那么我们怎么尽可能避免这个问题呢?
于是就有了代码四:

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

在代码四中表达式中的常量放在了左边,变量放在了右边。此时如果我们不小心少写了一个=,写成if(5 = num)就会直接报错,编译无法通过,更容易发现错误,减少bug。

在上述四个代码中,我们发现好的代码风格会减少代码的出错率,上述中代码二与代码四都是好的代码风格,通过总结可以归结为以下两点:

  • 通过适量的代码块({})来提升可读性
  • 在表达式中常量放在左边,变量放在右边,例如5==num

2.1.3练习题

  1. 判断一个数是否为奇数
  2. 输出1-100之间的奇数

题1:

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

题2:

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

2.2 switch语句

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

比如:
输入1,输出星期一

输入2,输出星期二

输入3,输出星期三

输入4,输出星期四

输入5,输出星期五

输入6,输出星期六

输入7,输出星期日

这种情况下使用if......else的形式会太复杂,那么我们就有了switch这种分支语句。

switch的语法结构:

switch(整型表达式)

{

    语句项;

}

在这里语句项是什么呢?

//是一些case语句
//如下:
case 整型常量表达式:
    语句;

2.2.1在switch语句中的break

我们来看一个代码想要实现

输入1,输出星期一

输入2,输出星期二

输入3,输出星期三

输入4,输出星期四

输入5,输出星期五

输入6,输出星期六

输入7,输出星期日

分支与循环结构_第1张图片

在这里我们发现结果与我们想要的不符,为什么呢?

因为在case语句中我们未加break导致case语句一直向下进行. 

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

即case表达式决定入口,break决定出口,跳出switch。

那这个代码应该怎么写?

#include 
int main()
{
	int a = 0;
	scanf("%d", &a);
	switch (a)
	{
	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;

}

分支与循环结构_第2张图片

 此时就达到了我们想要的结果.

那此时我们会想到一个问题:是不是每一个case后必须有break?

答案是不是,case后面有无break是根据具体逻辑来看.

如果要求写一个代码满足以下条件:

  1. 输入1-5,输出的是"weekday".
  2. 输入6-7,输出的是"weekend"
#include 
int main()
{
	int a = 0;
	scanf("%d", &a);
	switch (a)
	{
	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语句的实际效果时把语句列表划分为不同的分支部分.

2.2.2default子句

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

其实也没什么,结构就是所有的语句都被跳过而已.

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

这时就有了default子句.

当switch表达式的值并不匹配所有case标签的时候,这个default子句后面的语句就会执行,所以每一个switch语句中只能出现一条default子句.

注意,default可以出现在语句列表的任何位置,而且语句流会像执行一个case标签一样执行default子句.

2.2.3练习

下面代码的执行结果是什么:
 

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

运行结果是什么呢,接下来我们分析一下:

分支与循环结构_第3张图片

三、循环语句

  • while 

  • for

  • do    while

3.1while循环

当同一件事情需要做许多次时,在c语言中怎么办呢?

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

//while循环语法结构

while(表达式)     表达式为真,执行语句,执行后再返回while判断真假

       循环语句; 

例如我们实现:

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

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

     注意while循环是默认控制一条语句,若要控制多条语句则需要使用代码块({});

3.1.1while语句中的break和continue

在while循环中的break有什么作用呢?

我们用代码来说明:
 

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

此时运行结果为

分支与循环结构_第4张图片

我们发现5及5以后的数字没有被打印。

由此我们可以知道break执行的时候终止了循环,让循环不再继续。

总结: 

break在while循环中的作用:

  • 在循环中只要遇到break,就停止后期的所有循环,直接终止循环
  • while循环中的break适用于永久停止循环的

在while循环中的continue有什么作用呢?

我们也使用代码来解释:

1.

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

此处的运行结果是1 2 3 4然后进行死循环。

为什么会这样呢?那是因为continue在循环中是跳过本次循环continue后面的代码,直接去判断部分判断。

2.

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

分支与循环结构_第5张图片

总结:

continue在while循环中的作用是:

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

3.1.3scanf即getchar读取相关问题

我们再来看几个代码:
 

//代码一
#include
int main()
{
	int ch = 0;
	while ((ch = getchar()) != EOF)
		putchar(ch);
	return 0;
}

接下来我们来解析一下这个代码:

分支与循环结构_第6张图片

在这里我们从键盘输入一个字符,再通过putchar函数打印出来。

在这里我们发现从键盘输入字符的时候,当输入字符时会按下回车键,在程序运行结果中回车键也被打印出来了。这就说明getchar可以接收到回车键,putchar可以将回车键打印出来,那么他究竟有什么实际用途呢,下面我们通过一写代码来表示。

我们想要有一个代码实现以下功能:

  •  用户从键盘输入密码
  • 让用户确认密码

在接下来的代码中我们将会一步步改进直到达到想要的结果;

第一版:
 

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

	printf("请确认密码(Y/N):\n");
	int ch = getchar();

	if (ch == 'Y')
	{
		printf("确认成功\n");
	}
	else
	{
		printf("确认失败\n");
	}

	return 0;
}

运行结果为:

分支与循环结构_第7张图片 

在这里我们预想结果是输入密码然后输入Y或者N最终打印确认失败或者确认成功,但是在此处运行时我们发现在输入密码之后直接就确认失败了,这是为什么呢?

我们此处插入scanf以及getchar从键盘读取字符的相关知识:

   scanf工作原理:scanf要读数据,他首先去输入缓冲区看是否有数据,如果有数据就读取,如果没有数据,就等待键盘输入。
   getchar工作原理与scanf相同,都是先从缓冲区看,如果有数据就读取,如果没有数据,就等待键盘输入。

具体我们可以使用图解:

分支与循环结构_第8张图片

 注意的是:scanf从缓冲区读取时不会读取回车,回车仅仅是为了数据那能够放入缓冲区。

      在上述代码中,scanf先去输入缓冲区看是否有数据发现了有,就读取了hgsd即回车前面的字符,然后程序向下运行,运行到  int ch = getchar();语句时,getchar函数先去输入缓冲区看是否有数据,此时在输入缓冲区中有没有被scanf读取的回车键即‘'\n',这时getchar就读取了回车键,然后进入if  else语句中,发现回车即\n!=Y这时就会打印确认失败。

那么我们怎么改进这一问题呢?

      我们想scanf函数读取后,输入缓冲区里面留下了‘\n’所以getchar直接将其读取了,那么我们在int ch = getchar();语句之前可以再写一个getchar函数将缓冲区里面的回车读取了在之后的gechar函数读取时发现输入缓冲区里面没有数据,就会等待键盘输入,这样,问题就解决了,那么我们来看一下代码吧!

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

	getchar();
	printf("请确认密码(Y/N):\n");
	int ch = getchar();

	if (ch == 'Y')
	{
		printf("确认成功\n");
	}
	else
	{
		printf("确认失败\n");
	}
	return 0;
}

分支与循环结构_第9张图片

 此时的运行结果就对了!

刚刚我们说scanf不会读取使数据放入缓冲区的'\n',那么scanf在输入缓冲区读取时还有什么需要注意的点吗,当然是有的,scanf读取字符串时遇到空格时便不再读取,空格也不会被读取。

我们用代码来说明:

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

	getchar();
	printf("请确认密码(Y/N):\n");
	int ch = getchar();
	if (ch == 'Y')
	{
		printf("确认成功\n");
	}
	else
	{
		printf("确认失败\n");
	}
	return 0;
}

分支与循环结构_第10张图片

      当我们输入密码中有空格时,我们发现和第一次情况相同,在输入密码后直接输出了确认错误,此时有的同学可能会疑惑我们不是已经在 int ch = getchar();语句之前再写了一个getchar函数了吗为什么还会出错,要注意getchar函数只能读取一个字符,而在这里scanf未读取空格即之后的字符,字符数量多,当然会出错。

    那么我们又需要对代码进行改进:
    我们想到可以使用循环。

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

	//清理
	int tmp = 0;
	while ((tmp = getchar()) != '\n')
	{
		;      //读到'\n'且把'\n'读走停下来
	}
	printf("请确认密码(Y/N):\n");
	int ch = getchar();
	if (ch == 'Y')
	{
		printf("确认成功\n");
	}
	else
	{
		printf("确认失败\n");
	}
	return 0;
}

这时改进完成。

3.2 for循环

3.2.1 for循环的语法

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

     循环语句;

其中
表达式1:

表达式1是初始化部分,用于初始化循环变量的。

表达式2:
表达式2是条件判断部分,用于判断循环是否终止。

表达式3:
表达式3是调整部分,用于循环条件的调整。

我们来举个例子以便大家更好地理解for循环

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

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

在此处我们来比较一下while循环与for循环:
 

#include 
int main()
{
	int i = 0;
	
	//实现相同的功能,使用while
	i = 1;   //初始化部分
	while (i <= 10)
	{
		printf("haha\n");
		i = i + 1;  //调整部分
	}

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

我们可以发现在while循环中依然存在循环的三个必然条件,但是由于风格的问题使得三个部分很可能偏离较远,这样查找修改就不够集中和方便。

3.2.2 break和continue在for循环中

在for循环中break和continue的作用与在while循环中大致相同,但也有小部分不同。

我们使用代码来说明:
 

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

分支与循环结构_第11张图片

此时break含义任然是跳出整个循环。

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

 分支与循环结构_第12张图片

 此时continue的作用是跳过for循环{}中continue后面的代码,来到调整部分。这里与while循环中continue不同,while循环中continue是来到判断部分。

3.2.3 一些for循环的变种

代码一、

#include 
int main()
{
	for (; ; )
	{
		printf("haha\n");
	}
	return 0;
}

在上述代码中会一直死循环的一直输出haha

为什么呢?

//for循环中的初始化部分,判断部分、调整部分都是可以省略的,但是不建议初学者省略,容易导致问题。注意省略for循环中的判断部分就意味着判断恒为真。

代码二、

#include 
int main()
{
	int i = 0;
	int j = 0;
	for (i = 0; i < 10; i++)
	{
		for (j = 0; j < 10; j++)
		{
			printf("haha\n");
		}
	}
	return 0;
}

//代码运行结果是打印100次haha

如果上述代码中省略初始化部分,会打印多少次haha呢?
 

#include 
int main()
{
	int i = 0;
	int j = 0;
	for ( ; i < 10; i++)
	{
		for ( ; j < 10; j++)
		{
			printf("haha\n");
		}
	}
	return 0;
}

分支与循环结构_第13张图片

为什么会这样呢?
当我们省略了初始化部分for循环中嵌套的for循环执行完后j=10,不会初始化,此时外层的for循环再次i++后无法再次进入for循环中嵌套的循环。

代码三、

代码三说明可以使用多于1个变量来控制循环。

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

 3.2.4 一道小题

//请问循环要循环多少次?
#include
int main()
{
    int i = 0;
    int k = 0;
    for (i = 0, k = 0; k = 0; i++, k++)
        k++;
    return 0;
}

其实在上题中循环一次都不执行,在for循环的判断部分是一个赋值语句,赋值语句的结果就是所赋的值,即上述判断部分为0,0表示假,循环一次都不执行。

3.3 do......while()循环

3.3.1do......while 循环的语法

do

     循环语句;

while(表达式);

3.3.2 特点

循环至少会执行一次。

注意do后面需要执行多条语句时,需要加一个代码块({})。

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

3.3.3 do   while循环中的break和continue

在do  while循环中break与continue的作用与while循环中相似

我们通过代码来表现:

1.

#include 
int main()
{
	int i = 1;
	do
	{
		if (5 == i)
			break;     //永久的终止循环
		printf("%d ", i);
		i++;
	} while (i <= 10);
	return 0;
}

 

分支与循环结构_第14张图片

2.

#include 
int main()
{
	int i = 1;

	do
	{
		if (5 == i)
			continue;  //跳过本次循环之后的代码去判断部分  
		printf("%d ", i);
		i++;
	} while (i <= 10);
	return 0;
}

//运行结果是1 2 3 4 然后死循环

3. 

#include 
int main()
{
	int i = 0;
	do
	{
		i++;
		if (5 == i)
			continue;  //跳过本次循环之后的代码去判断部分  
		printf("%d ", i);
		
	} while (i < 10);
	return 0;
}

分支与循环结构_第15张图片

 四、goto语句

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

goto语句最常见的用法是终止程序在某些深度嵌套的结构的处理过程。

goto语句真正适合的场景

for(...)

     for(...)

     {

           for(...)

           {
               if(disaster)

                goto error;
           }     

      }

     ...

error:

      if(disaster)

//处理错误情况

一个关机程序

在写程序时我们先简单介绍一下:
//shutdown -s -t 60   表示的是60s之后关机

//其中shutdown -s是电脑中的关机命令 后面加-t是设置关机时间

//shutdown -a表示的是取消关机

使用goto语句写:

#include 
#include 
#include 
int main()
{
	char input[10] = { 0 };
	system("shutdown -s -t 60");  //system函数用于执行系统命令的库函数

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

使用while循环写:
 

#include 
#include 
#include
int main()
{
	char input[10] = { 0 };
	system("shutdown -s -t 60");  //system函数用于执行系统命令的库函数

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

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