3.1 C语言之控制流语句

发布时间:2023年12月24日

一、控制流概述

所谓控制流语句就是用来控制程序指令执行的次序,比如,如果条件满足执行语句A,否则,执行语句B。
在讲述具体的控制流语句之前,先了解一下语句和程序块是有必要的

  • 语句:在x=0、x++、printf(…)这样的表达式之后加上一个分号( ; ),它们就变成了语句,例如:
	x = 0;
	x++;
	printf("Hello");
     在C语言中,分号是语句结束符。
  • 程序块:使用一对花括号{}把一组声明和语句括起来就是程序块,例如:
if(a > 0) {   // 程序块开始
      int x, y;
      x = 1;
      y = x + 2;
}  // 程序块结束,} 后面不需要加分号

二、if-else语句

if-else语句用于条件判定,表示如果…,否则…,其语法如下:

if(表达式)
   语句1;
else
   语句2;

其中,else部分是可选的。该程序执行时,先计算表达式的值,如果其值为真(即表达式的值非0),则执行语句1,如果其值为假(即表达式的值为0),则执行语句2。
看一个例子:

#include <stdio.h>

int main()
{
	int n = 5;
	char s[] = "hello";
	if (n > 0)
		for(int i =0; i<n;i++)
			if(s[i] > 0)
				printf("%c", s[i]);
	else
		printf("n为负数");
	return 0;
}

如上程序,从缩进结构可以明确的表明设计意图,但编译器不一定能get到,它可能会将else与内层的if配对,这种错误是很难发现的,因此我们建议在有if语句嵌套的情况下使用花括号。例如:

#include <stdio.h>

int main()
{
	int n = 5;
	char s[] = "hello";
	if (n > 0) {
		for (int i = 0; i < n;i++) {
			if (s[i] > 0)
				printf("%c", s[i]);
		}
	} 
	else
		printf("n为负数");
	return 0;
}

三、else-if语句

else-if用于多路判定

if(表达式)
   语句;
else if(表达式)
   语句;
else if(表达式)
   语句;
else if(表达式)
   语句;
else
   语句;    

上述程序中的各表达式将被依次求值,一旦某个表达式的结果为真,执行与之对应的语句,并终止整个语句序列。例如:

#include <stdio.h>

int main()
{
	int score = 70;
	if (score >= 90) {
		printf("优秀");
	} else if (score >= 80) {
		printf("良好");
	} else if (score >= 60) {
		printf("及格");
	} else {
		printf("不及格");
	}
	return 0;
}

四、switch语句

switch语句用于多路判定,它测试表达式是否与给定的一些常量整数值中的某一个值匹配,并执行相应的分支动作。

switch(表达式)
    case 常量表达式: 语句序列
    case 常量表达式: 语句序列
    default: 语句序列
  • default分支是可选的
  • 如果某个分支与表达式的值匹配,则从该分支开始执行
  • 各表达式必须互不相同
  • 如果没有case分支能匹配表达式的值,则默认执行default分支,如果没有default,则switch语句不执行任何动作
  • 各分支及default的分支排列次序是任意的(即使如此,请将default放到最后)
    看一个例子:编写一个程序,统计输入中数字、空白符、其他字符出现的次数。
#include <stdio.h>

// 编写一个程序,统计输入中各个数字(0-9)、空白符(空格,水平制表符,换行符)、其他字符出现的次数。
int main()
{
	int ndigit[10];  // 存放各个数字出现的次数
	int nwhite;  // 空白符出现的次数
	int nother;  // 其他字符出现的次数
	for (int i = 0; i < 10;i++)
		ndigit[i] = 0;
	nwhite = nother = 0;
	char c;  // 输入的字符
	while ((c = getchar()) != EOF) {
		switch (c) {
			case '0': case '1':
			case '2': case '3':
			case '4': case '5':
			case '6': case '7':
			case '8': case '9':
				ndigit[c - '0']++;
				break;
			case ' ':
			case '\t':
			case '\n':
				nwhite++;
				break;
			default:
				nother++;
				break;
		}
	}
	for (int i = 0; i < 10;i++) {
		printf("%d出现的次数:%d\n", i, ndigit[i]);
	}
	printf("空白符出现的次数:%d\n", nwhite);
	printf("其他字符出现的次数:%d\n", nother);
	return 0;
}
  • case只是一个标号,所以,执行完一个case,程序会接着执行下一个case,除非在程序中显式跳转,这与else if有所不同
  • 跳出switch语句最常用的方法是使用break语句与return语句
  • break语句的作用是立即退出switch,不再执行switch中其他分支
  • 依次执行各分支有优点也有缺点。好的一面是它可以把若干个分支组合在一起完成一个任务,如上面的统计各数字的次数,不好的地方在于这样的程序修改时容易出错。所以除了组合分支外,其他分支都应该加上break以明确表示switch的退出
  • 作为一种良好的程序设计风格,在switch语句最后一个分支(即default)的后面也加上break语句。这样做在逻辑上没有必要,但当我们需要向该switch添加其他分支时,这种防范措施会降低出错误的可能性。

五、while循环与for循环

while(表达式)
    语句
  • while循环,先测试表达式,如果表达式的值为真,进入循环体,执行语句,接着继续测试表达式,直至表达式的值为0,退出循环,执行循环后面的其他程序
for(表达式1;表达式2;表达式3)
    语句  
它等价于
表达式1
while(表达式2)
   语句 
   表达式3  
  • for循环,先执行表达式1,再测试表达式2,如果表达式2的值为真,执行语句,然后再执行表达式3,接着继续测试表达式2,直到表达式2的值为0,退出循环,执行循环后面的其他程序
  • 最常见的情况是,表达式1与表达式3是赋值表达式或函数调用,表达式2是关系表达式
    这三个表达式中的任何一个都可以省略,但分号必须保留,如果在for语句中省略表达式1与表达式3,它就退化成for循环语句。如果省略表达式2,则认为其值永远为真,因此如下代码
    for( ; ; ) {

    }
    是一个无限循环语句,这种语句需要借助break或return语句才能终止执行。
  • 在设计程序时到底选用for循环还是while循环,主要取决于程序设计人员的个人偏好。例如,
    while((c = getchar()) == ’ ’ || c==‘\n’ || c==‘\t’)
    因为没有初始化或重新初始化的操作,所以使用while更自然些
    如果语句中需要执行简单的初始化和变量递增,使用for更合适一些,它将循环控制语句集中放到循环的开头,结构更紧凑、更清晰,例如,
    for(int i = 0; i<n; i++)

六、do-while循环

while和for这两种循环在循环体执行前对终止条件进行测试,与此相反,do-while循环在循环体执行之后测试终止条件,这样,循环体至少被执行一次。

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

先执行循环体,再测试表达式,如果表达式为真,继续执行循环体,直到表达式结果为假,退出循环

七、break语句和continue语句

在需要的时候提前退出循环,是很有用的操作。break语句可以用于从for、while、do-while循环中提前退出,就如同在switch中提前退出一样。

  • break能使程序从switch语句或最内层循环中立即跳出
  • continue语句用于使for、while、do-while语句开始下一次循环的执行;在while与do-while循环中,continue意味着立即执行测试条件,而在for循环中,则意味着控制转移到递增循环变量部分。
  • continue只能用于循环,不用于switch,某个循环包含的switch语句中的continue,将导致进入下一次循环

八、goto

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

#include <stdio.h>

int main()
{
	int r = 0;
	for (;;)
		for (int i = 0; i < 10; i++)
			if (i > 5) {
				r = i;
				goto error;
			}
error:
	printf("%d", r);
	return 0;
}
  • 如果错误处理代码很重要,并且错误可能出现在多个地方,使用goto语句会比较方便。
  • error为标号,命名同变量的命名形式,标号的后面要紧跟一个冒号
  • 标号的作用域是整个函数

再看一个例子,考虑判定两个数组a和b中是否有相同元素的问题。一种可能的解决办法是

#include <stdio.h>

int main()
{
	int a[10];
	int b[6];
	for (int i = 0;i < 10; i++) {
		a[i] = 1;
	}
	for (int i = 0;i < 6; i++) {
		if (i = 5) {
			b[i] = 1;
		}
		else {
			b[i] = 2;
		}
	}
	int ia = 0;
	int ib = 0;
	int res = 0;
	for (int i = 0; i < 10; i++)
		for (int j = 0; j < 6; j++)
			if (a[i] == b[j]) {
				ia = i;
				ib = j;
				res = a[i];
				goto found;
			}

found:
	printf("%d\t%d\t%d", ia, ib, res);
	return 0;
}

所有使用了goto的语句都可以改写成不带goto,但可能会增加一些额外的测试或变量,例如,上面的程序可以改写成:

#include <stdio.h>

int main()
{
	int a[10];
	int b[6];
	for (int i = 0;i < 10; i++) {
		a[i] = 1;
	}
	for (int i = 0;i < 6; i++) {
		if (i = 5) {
			b[i] = 1;
		}
		else {
			b[i] = 2;
		}
	}
	int ia = 0;
	int ib = 0;
	int res = 0;
	int found = 0;
	for (int i = 0; i < 10; i++)
	{
		if (found) {
			break;
		}
		for (int j = 0; j < 6; j++)
			if (a[i] == b[j]) {
				ia = i;
				ib = j;
				res = a[i];
				found = 1;
				break;
			}
	}
	if(found)
	    printf("%d\t%d\t%d", ia, ib, res);
	return 0;
}

大多数情况下,使用goto的程序比没有使用goto的程序要难以理解和维护,少数情况除外,比如我们前面的几个例子。尽管问题并不太严重,但我们还是建议尽可能少的使用goto语句。

文章来源:https://blog.csdn.net/unwrapping/article/details/135170947
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。