C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记

第七章 C控制语句:分支和跳转

  • 7.1 if 语句
  • 7.2 if else 语句
      • 7.2.1 另一个示例:介绍 getchar() 和 putchar()
      • 7.2.2 ctype.h 系列的字符函数
      • 7.2.3 多重选择 else if
      • 7.2.4 else 与 if 配对
      • 7.2.5 多层嵌套的 if 语句
  • 7.3 逻辑运算符
      • 7.3.1 备选拼写:iso646.h 头文件
      • 7.3.2 优先级
      • 7.3.3 求值顺序
      • 7.3.4 范围
  • 7.4 一个统计单词的程序
  • 7.5 条件运算符:?:
  • 7.6 循环辅助:continue 和 break
      • 7.6.1 continue 语句
      • 7.6.2 break 语句
  • 7.7 多重选择:switch 和 break
      • 7.7.1 switch 语句
      • 7.7.2 只读每行的首字母
      • 7.7.3 多重标签
      • 7.7.4 switch 和 if else
  • 7.8 goto 语句
      • 7.8.1 避免使用 goto
  • 7.9 关键概念
  • 7.10 本章小结

7.1 if 语句

  if 语句被称为分支语句或选择语句,因为它相当于一个交叉点,程序要在两条分支中选择一条执行。if语句的通用形式如下:
  if ( expression )
   statement
  如果对expression求值为真(非0),则执行statement;否则,跳过statement。与while循环一样,statement可以是一条简单语句或复合语句。if 语句的结构和while语句很相似,它们的主要区别是:如果满足条件可执行的话,if 语句只能测试和执行一次,而while语句可以测试和执行多次。
  expression 可以使用任意表达式,表达式的值为0则为假。
  statement 可以是一条简单语句,也可以是复合语句。注意,即使 if 语句由复合语句构成,整个 if 语句仍被视为一条语句。

colddays.c程序:读取一列数据,每个数据都表示每日的最低温度(℃),然后打印统计的总天数和最低温度在0℃以下的天数占总天数的百分比。程序中的循环通过scanf()读入温度值。while循环每迭代一次,就递增计数器增加天数,其中的 if 语句负责判断0℃以下的温度并单独统计相应的天数。

#include 
int main(void)
{
	const int FREEZING = 0; 
	float temperature;
	int cold_days = 0;
	int all_days = 0;
	
	printf("Enter the list of daily low temperature.\n");
	printf("Use Celsius, and enter q to quit.\n");
	while(scanf("%f", &temperature) == 1)
	{
		all_days++;
		if(temperature<FREEZING)
		{
			cold_days++;
		}
	}
	if(all_days != 0)
	{
		printf("%d days total: %.1f%% were below freezing.\n", all_days, 100.0*(float)cold_days/all_days);
	}
	if(all_days == 0)
	{
		printf("No data entered!\n");
	}
	
	return 0;
 } 

运行结果:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第1张图片
程序说明:
  为避免整数除法,该程序示例把计算后的百分比强制转换为 float 类型。其实,也不必使用强制类型转换,因为在表达式100.0 * cold_days /all_days中,将首先对表达式100.0 * cold_days求值,由于C的自动转换类型规则,乘积会被强制转换成浮点数。但是,使用强制类型转换可以明确表达转换类型的意图,保护程序免受不同版本编译器的影响。

7.2 if else 语句

  if 语句用于选择是否执行一个行为,而 if else语句用于在两个行为之间选择。
  if else语句的通用形式是:
  if ( expression )
    statement1
  else
    statement2
  如果expression为真(非0),则执行 statement1;如果expression为假或0,则执行else后面的 statement2。statement1 和 statement2 可以是一条简单语句或复合语句。

7.2.1 另一个示例:介绍 getchar() 和 putchar()

  字符输入\ 输出函数:getchar() 和 putchar()

  getchar()函数不带任何参数,它从输入队列中返回下一个字符。例如,下面的语句读取下一个字符输入,并把该字符的值赋给变量ch:
  ch = getchar();
  该语句与下面的语句效果相同:
  scanf("%c", &ch);

  putchar()函数打印它的参数。例如,下面的语句把之前赋给ch的值作为字符打印出来:
  putchar(ch);
  该语句与下面的语句效果相同:
  printf("%c", ch);

  由于这些函数只处理字符,所以它们比更通用的 scanf() 和 printf() 函数更快、更简洁。这两个函数通常定义在 stdio.h头文件中。

cypher1.c程序:把一行输入重新打印出来,但是每个非空格都被替换成原字符在ASCII序列中的下一个字符,空格不变。这一过程可描述为“如果字符是空白,原样打印;否则,打印原字符在ASCII序列中的下一个字符。

#include 
#define SPACE ' '				// SPACE表示单引号-空格-单引号 
int main(void)
{
	char ch;
	
	ch = getchar();				// 读取一个字符
	while(ch != '\n')			// 当一行未结束时 
	{
		if(ch == SPACE)			// 留下空格
			putchar(ch);		// 该字符不变
		else 
			putchar(ch + 1);	// 改变其他字符
		ch = getchar();			// 获取下一个字符 
	}
	putchar(ch);				// 打印换行符
	
	return 0; 
 }

运行结果:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第2张图片
程序说明:
   把colddays.c程序中的循环和该例中的循环作比较。前者使用scanf()返回的状态值判断是否结束循环,而后者使用输入项的值来判断是否结束循环。这使得两程序所用的循环结构略有不同:colddays.c程序中在循环前面有一条“读取语句”,程序清单7.2中在每次迭代的末尾有一条“读取语句”。
   putchar(ch + 1); 再次演示了字符实际上是作为整数储存的。为方便计算,表达式ch + 1中的ch被转换成int类型,然后int类型的计算结果被传递给接受一个int类型参数的putchar()。putchar()只根据最后一个字节确定显示哪个字符。

7.2.2 ctype.h 系列的字符函数

  C 有一系列专门处理字符的函数,ctype.h头文件包含了这些函数的原型。这些函数接受一个字符作为参数,如果该字符属于某特殊的类别,就返回一个非零值(真);否则,返回0(假)。例如,如果isalpha()函数的参数是一个字母,则返回一个非零值。

cypher2.c程序:替换输入的字母,非字母字符保持不变

#include 
#include 
int main(void)
{
	char ch;
	
	while((ch = getchar())!='\n')
	{
		if(isalpha(ch))		// 如果是一个字符
			putchar(ch+1);	// 显示该字符的下一个字符
		else				// 否则 
			putchar(ch);	// 原样显示 
	}
	putchar(ch);			// 显示换行符
	
	return 0; 
 } 

运行结果:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第3张图片
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第4张图片
  表7.1和表7.2列出了ctype.h头文件中的一些函数。有些函数涉及本地化,指的是为适应特定区域的使用习惯修改或扩展 C 基本用法的工具(例如,许多国家在书写小数点时,用逗号代替点号,于是特殊的本地化可以指定C编译器使用逗号以相同的方式输出浮点数,这样123.45可以显示为123,45)。注意,字符映射函数不会修改原始的参数,这些函数只会返回已修改的值。也就是说,下面的语句不改变ch的值:

tolower(ch);   // 不影响ch的值
这样做才会改变ch的值:
ch = tolower(ch);    // 把ch转换成小写字母

7.2.3 多重选择 else if

  在程序中可以用 else if 扩展 if else 结构模拟多种选择。

electric.c程序:计算电费。电力公司通常根据客户的总用电量来决定电费。下面是某电力公司的电费清单,单位是千瓦时(kWh):
       首 360kWh:    $0.13230/kWh
       续 108kWh:    $0.15040/kWh
       续 252kWh:    $0.30025/kWh
       超过 720kWh:   $0.34025/kWh

#include 
#define RATE1 0.13230			// 首次使用 360 kwh 的费率
#define RATE2 0.15040			// 接着再使用 108 kwh 的费率 
#define RATE3 0.30025			// 接着再使用 252 kwh 的费率
#define RATE4 0.34025			// 使用超过 720 kwh 的费率
#define BREAK1 360.0			// 费率的第 1个分界点 
#define BREAK2 468.0			// 费率的第 2个分界点 
#define BREAK3 720.0			// 费率的第 3个分界点 
#define BASE1 (RATE1 * BREAK1)
// 使用360kwh的费用
#define BASE2 (BASE1 + (RATE2 * (BREAK2 - BREAK1)))
// 使用468kwh的费用
#define BASE3 (BASE1 + BASE2 + (RATE3 *(BREAK3 - BREAK2)))
// 使用720kwh的费用
int main(void)
{
	double kwh;                 // 使用的千瓦时
	double bill;                // 电费
	
	printf("Please enter the kwh used.\n");
	scanf("%lf", &kwh);			// %lf对应double类型
	if (kwh <= BREAK1)
		bill = RATE1 * kwh;
	else if (kwh <= BREAK2)     // 360~468 kwh
	    bill = BASE1 + (RATE2 * (kwh - BREAK1));
	else if (kwh <= BREAK3)     // 468~720 kwh
		bill = BASE2 + (RATE3 * (kwh - BREAK2));
	else                        // 超过 720 kwh
		bill = BASE3 + (RATE4 * (kwh - BREAK3));
	printf("The charge for %.1f kwh is $%1.2f.\n", kwh, bill);
	
	return 0;
}

运行结果:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第5张图片
程序说明:
  该程序根据kwh的值在3个公式之间选择一个。特别要注意的是,如果kwh大于或等于360,程序只会到达第1个else。因此,else if (kwh <= BREAK2)这行相当于要求kwh在360~468之间,如程序注释所示。类似地,只有当kwh的值超过720时,才会执行最后的else。最后,注意BASE1、BASE2和BASE3分别代表360、468和720千瓦时的总费用。因此,当电量超过这些值时,只需要加上额外的费用即可。

  对于编译器的限制范围,C99标准要求编译器最少支持127层套嵌。

7.2.4 else 与 if 配对

  如果没有花括号,else与离它最近的 if 匹配,除非最近的 if 被花括号括起来。
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第6张图片

7.2.5 多层嵌套的 if 语句

  前面介绍的if…else if…else序列是嵌套if的一种形式,从一系列选项中选择一个执行。有时,选择一个特定选项后又引出其他选择,这种情况可以使用另一种嵌套 if。例如,程序可以使用 if else选择男女,if else的每个分支里又包含另一个if else来区分不同收入的群体。

divisors.c程序:给定一个整数,显示所有能整除它的约数。如果没有约数,则报告该数是一个素数。

#include 
#include 
int main(void)
{
	unsigned long num;		// 待测试的数
	unsigned long div;		// 可能的约数
	bool isPrime;			// 素数标记
	
	printf("Please enter an integer for analysis; ");
	printf("Enter q to quit.\n");
	while(scanf("%lu", &num)==1)
	{
		for(div = 2, isPrime = true; (div*div) <= num; div++)
		{
			if(num % div == 0)
			{
				if((div * div) != num)
					printf("%lu is divisible by %lu and %lu.\n",num, div, num/div);
				else 
					printf("%lu is divisible by %lu.\n",num, div);
				isPrime = false;	// 该数不是素数 
			}
		} 
		if(isPrime)
			printf("%lu is prime.\n", num);
		printf("Please enter another integer for analysis; ");
		printf("Enter q to quit.\n"); 
	} 
	printf("Bye.\n");
	
	return 0;
}

运行结果:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第7张图片
程序说明:
  该程序会把1认为是素数,其实它不是。下一节将要介绍的逻辑运算符可以排除这种特殊的情况。

7.3 逻辑运算符

chcount.c程序:计算输入的一行句子中除单引号和双引号以外其他字符的数量。这种情况下可以使用逻辑运算符,并使用句点(.)标识句子的末尾。

// chcount.c -- 使用逻辑与运算符
#include 
#define PERIOD '.'
int main(void)
{
	char ch;
	int charcount = 0;
	
	while((ch = getchar()) != PERIOD)
	{
		if(ch != '"' && ch != '\'')
			charcount++;
	}
	printf("There are %d non-quote characters.\n", charcount);
	
	return 0;
} 

运行结果:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第8张图片
程序说明:
  首先读入一个字符,并检查它是否是一个句点,因为句点标志一个句子的结束。接下来,if 语句的测试条件中使用了逻辑与运算符&&。该 if 语句翻译成文字是“如果待测试的字符不是双引号,并且它也不是单引号,那么charcount递增1”。
  逻辑运算符两侧的条件必须都为真,整个表达式才为真。逻辑运算符的优先级比关系运算符低,所以不必在子表达式两侧加圆括号。

c 有3种逻辑运算符:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第9张图片
假设exp1和exp2是两个简单的关系表达式(如car > rat 或 debt ==1000),那么:
  当且仅当exp1和exp2都为真时,exp1 && exp2才为真;
  如果exp1或exp2为真,则exp1 || exp2为真;
  如果exp1为假,则!exp1为真;如果exp1为真,则!exp1为假。

7.3.1 备选拼写:iso646.h 头文件

  C 是在美国用标准美式键盘开发的语言。但是在世界各地,并非所有的键盘都有和美式键盘一样的符号。因此,C99标准新增了可代替逻辑运算符的拼写,它们被定义在ios646.h头文件中。如果在程序中包含该头文件,便可用 and 代替 && 、or 代替 || 、not 代替 ! 。

下图列出逻辑运算符对应的拼写:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第10张图片

7.3.2 优先级

  ! 运算符的优先级很高,比乘法运算符还高,与递增运算符的优先级相同,只比圆括号的优先级低。&&运算符的优先级比 || 运算符高,但是两者的优先级都比关系运算符低,比赋值运算符高。因此,表达式 a >b && b > c || b > d 相当于 ((a > b) && (b > c)) || (b > d)。

7.3.3 求值顺序

  C 通常不保证先对复杂表达式中哪部分求值。例如,下面的语句,可能先对表达式5 + 3求值,也可能先对表达式9 + 6求值:
  apples = (5 + 3) * (9 + 6);
  C 把先计算哪部分的决定权留给编译器的设计者,以便针对特定系统优化设计。但是,对于逻辑运算符是个例外,C保证逻辑表达式的求值顺序是从左往右。&& 和 || 运算符都是序列点,所以程序在从一个运算对象执行到下一个运算对象之前,所有的副作用都会生效。而且,C 保证一旦发现某个元素让整个表达式无效,便立即停止求值。

7.3.4 范围

  && 运算符可用于测试范围。

  许多代码都用范围测试来确定一个字符是否是小写字母。例如,假设ch是char类型的变量:

  if (ch >= ‘a’ && ch <= ‘z’)
    printf(“That’s a lowercase character.\n”);

  该方法仅对于像ASCII这样的字符编码有效,这些编码中相邻字母与相邻数字一一对应。但是,对于像EBCDIC这样的代码就没用了。相应的可移植方法是,用ctype.h系列中的islower()函数:

  if (islower(ch))
    printf(“That’s a lowercase character.\n”);

  无论使用哪种特定的字符编码,islower()函数都能正常运行

7.4 一个统计单词的程序

wordcnt.c程序:统计字符数、单词数、行数

#include  
#include 			// 为 isspace()函数提供原型
#include 		// 为 bool、true、false提供定义
#define STOP '|'
int main(void)
{
	char c;					// 读入字符
	char prev;				// 读入的前一个字符
	long n_chars = 0L;		// 字符数
	int n_lines = 0;		// 行数
	int n_words = 0;		// 单词数
	int p_lines = 0;		// 不完整的行数
	bool inword = false;	// 如果c在单词中,inword等于true
	
	printf("Enter text to be analyzed(| to terminate):\n");
	prev = '\n';			// 用于识别完整的行
	while((c = getchar()) != STOP)
	{
		n_chars++;			// 统计字符
		if(c=='\n')
			n_lines++;		// 统计行
		if(!isspace(c) && !inword)
		{
			inword = true;	// 开始一个新的单词
			n_words++;		// 统计单词 
		}
		if(isspace(c) && inword)
			inword = false;	// 达到单词的末尾
		prev = c;			// 保存字符的值 
	}
	
	if(prev != '\n')
		p_lines = 1;
	printf("characters = %ld, words = %d, lines = %d, ", n_chars, n_words, n_lines);
	printf("partial lines = %d\n", p_lines);
	
	return 0; 
} 

运行结果:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第11张图片
程序说明:
  STOP表示能标识输入末尾的某个值。我们选用 | 作为输入的末尾标记。
  因为该程序使用getchar()进行输入,所以每次迭代都要通过递增计数器来计数。为了统计行数,程序要能检查换行字符。如果输入的字符是一个换行符,该程序应该递增行数计数器。这里要注意STOP 字符位于一行的中间的情况。是否递增行数计数?我们可以作为特殊行计数,即没有换行符的一行字符。可以通过记录之前读取的字符识别这种情况,即如果读取时发现 STOP 字符的上一个字符不是换行符,那么这行就是特殊行。
  把一个单词定义为一个不含空白(即,没有空格、制表符或换行符)的字符序列。程序读取的第 1 个非空白字符即是一个单词的开始,当读到空白字符时结束。
  使用ctype.h头文件中的函数isspace()更简单,如果该函数的参数是空白字符,则返回真。所以,如果c是空白字符,isspace( c )为真;如果c不是空白字符,! isspace( c )为真。
  要查找一个单词里是否有某个字符,可以在程序读入单词的首字符时把一个标记(名为 inword)设置为1。也可以在此时递增单词计数。然后,只要inword为1(或true),后续的非空白字符都不记为单词的开始。下一个空白字符,必须重置标记为0(或false),然后程序就准备好读取下一个单词。
  在读到每个单词的开头时把inword设置为1(真),在读到每个单词的末尾时把inword设置为0(假)。只有在标记从0设置为1时,递增单词计数。

7.5 条件运算符:?:

  条件运算符:? :
  条件运算符需要3个运算对象,每个运算对象都是一个表达式。其通用形式如下:
  expression1 ? expression2 : expression3
  如果expression1为真,整个条件表达式的值是expression2的值;否则,是expression3的值。

paint.c程序:该程序计算刷给定平方英尺的面积需要多少罐油漆。基本算法很简单:用平方英尺数除以每罐油漆能刷的面积。但是,商店只卖整罐油漆,不会拆分来卖,所以如果计算结果是1.7罐,就需要两罐。因此,该程序计算得到带小数的结果时应该进1。条件运算符常用于处理这种情况,而且还要根据单复数分别打印can和cans。

#include  
#define COVERAGE 350	// 每罐油漆可刷的面积(单位:平方英尺)
int main(void)
{
	int sq_feet;
	int cans;
	
	printf("Enter number of square feet to be painted:\n");
	while(scanf("%d", &sq_feet) == 1)
	{
		cans = sq_feet / COVERAGE;
		cans += ((sq_feet % COVERAGE == 0))?0:1;
		printf("You need %d %s of paint.\n", cans, cans == 1 ? "can" :"cans" );
		printf("Enter next value (q to quit):\n");
	}
	
	return 0;
 }

运行结果:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第12张图片
程序说明:
  该程序使用的变量都是int类型,除法的计算结果(sq_feet /COVERAGE)会被截断。也就是说, 351/350得1。所以,cans被截断成整数部分。如果sq_feet % COVERAGE得0,说明sq_feet被COVERAGE整除,cans的值不变;否则,肯定有余数,就要给cans加1。
  printf()函数中的参数也是一个条件表达式,如果cans的值是1,则打印can;否则,打印cans。这也说明了条件运算符的第2个和第3个运算对象可以是字符串。

7.6 循环辅助:continue 和 break

  一般而言,程序进入循环后,在下一次循环测试之前会执行完循环体中的所有语句。continue 和break语句可以根据循环体中的测试结果来忽略一部分循环内容,甚至结束循环。

7.6.1 continue 语句

  3种循环都可以使用continue语句。执行到该语句时,会跳过本次迭代的剩余部分,并开始下一轮迭代。如果continue语句在嵌套循环内,则只会影响包含该语句的内层循环

skippart.c程序:

#include 
int main(void)
{
	const float MIN = 0.0f;
	const float MAX = 100.0f;
	
	float score;
	float total = 0.0f;
	int n = 0;
	float min = MAX;
	float max = MIN;
	
	printf("Enter the first score(q to quit): ");
	while(scanf("%f", &score) == 1)
	{
		if(score < MIN || score > MAX)
		{
			printf("%0.1f is an invalid value. Try again: ",score);
			continue;	// 跳转至while循环的测试条件 
		}
		printf("Accepting %0.1f:\n", score);
		min = (score < min) ? score : min;
		max = (score > max) ? score : max;
		total += score;
		n++;
		printf("Enter next score(q to quit): "); 
	}
	if(n > 0)
	{
		printf("Average of %d scores is %0.1f.\n", n, total / n);
		printf("Low = %0.1f, high = %0.1f\n", min, max); 
	}
	else
		printf("No valid scores were entered.\n");
		
	return 0;
 } 

运行结果:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第13张图片
程序说明:
  while循环读取输入,直至用户输入非数值数据。循环中的 if 语句筛选出无效的分数。假设输入 188,程序会报告:188 is an invalid value。在本例中,continue 语句让程序跳过处理有效输入部分的代码。程序开始下一轮循环,准备读取下一个输入值。

对于 while 和 do while 循环,执行 continue 语句后的下一个行为是对循环的测试表达式求值。
对于 for 循环,执行 continue 后的下一个行为是对更新表达式求值,然后是对循环测试表达式求值。

7.6.2 break 语句

  程序执行到循环中的break语句时,会终止包含它的循环,并继续执行下一阶段。如果break语句位于嵌套循环内,它只会影响包含它的当前循环。
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第14张图片
break.c程序:用一个循环计算矩形的面积。如果用户输入非数字作为矩形的长或宽,则终止循环。

#include 
int main(void)
{
	float length, width;
	
	printf("Enter the length of the rectangle:\n");
	while(scanf("%f", &length)==1)
	{
		printf("Length = %0.2f:\n", length);
		printf("Enter its width:\n");
		if(scanf("%f", &width) != 1)
			break;
		printf("Width = %0.2f:\n", width);
		printf("Area = %0.2f:\n", length * width);
		printf("Enter the length of the rectangle:\n");
	}
	printf("Done.\n");
	
	return 0;
 } 

运行结果:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第15张图片

7.7 多重选择:switch 和 break

  使用条件运算符和 if else 语句很容易编写二选一的程序。然而,有时程序需要在多个选项中进行选择。可以用if else if…else来完成。但是,大多数情况下使用 switch 语句更方便。

animals.c程序:读入一个字母,然后打印出与该字母开头的动物名。

/* animals.c -- 使用 switch 语句 */
#include 
#include 
int main(void)
{
	char ch;
	
	printf("Give me a letter of the alphabet, and I will give ");
	printf("an animal name\nbeginning with that letter.\n");
	printf("Please type in a letter; type # to end my act.\n");
	while((ch = getchar()) != '#')
	{
		if('\n' == ch)
			continue;
		if(islower(ch))		/* 只接受小写字母 */
			switch(ch)
		{
			case 'a':
				printf("argali, a wild sheep of Asia\n");
            	break;
			case 'b':
				printf("babirusa, a wild pig of Malay\n");
				break;
			case 'c':
				printf("coati, racoonlike mammal\n");
				break;
			case 'd':
				printf("desman, aquatic, molelike critter\n");
				break;
			case 'e':
				printf("echidna, the spiny anteater\n");
				break;
			case 'f':
				printf("fisher, brownish marten\n");
				break;
			default:
				printf("That's a stumper!\n");
						/* switch结束 */
		}
		else 
			printf("I recognize only lowercase letters.\n");
		while(getchar() != '\n')   
			continue;	/* 跳出输入行的剩余部分 */
		printf("Please type another letter or a #.\n");
						/* while循环结束 */	 
	}
	printf("Bye!\n");
	
	return 0; 
}

运行结果:
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第16张图片
程序说明:
  continue 可用作占位符。例如,循环读取并丢弃输入的数据,直至读到行末尾:
  while (getchar() != ‘\n’)
  continue;
  当程序已经读取一行中的某些内容,要跳至下一行开始处时,这种用法很方便。

7.7.1 switch 语句

  要对紧跟在关键字 switch 后圆括号中的表达式求值,然后程序扫描标签列表,直到发现一个匹配的值为止。然后程序跳转至那一行。如果没有匹配的标签怎么办?如果有default :标签行,就跳转至该行;否则,程序继续执行在switch后面的语句。

  break让程序离开switch语句,跳至switch语句后面的下一条语句(见下图)。如果没有break语句,就会从匹配标签开始执行到switch末尾。
C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记_第17张图片
  break语句可用于循环和switch语句中,但是continue只能用于循环中。尽管如此,如果switch语句在一个循环中,continue便可作为switch语句的一部分。
  switch在圆括号中的测试表达式的值应该是一个整数值(包括char类型)。case标签必须是整数类型(包括char类型)的常量或整型常量表达式(即,表达式中只包含整型常量)。不能用变量作为case标签。

7.7.2 只读每行的首字母

  animals.c程序当输入dab时,只处理了第1个字符。这种丢弃一行中其他字符的行为,经常出现在响应单字符的交互程序中。可以用下面的代码实现这样的行为:
  while (getchar() != ‘\n’)
  continue;    /* 跳过输入行的其余部分 */
  循环从输入中读取字符,包括按下Enter键产生的换行符。注意,函数的返回值并没有赋给ch,以上代码所做的只是读取并丢弃字符。由于最后丢弃的字符是换行符,所以下一个被读取的字符是下一行的首字母。在外层的while循环中,getchar()读取首字母并赋给ch。
  假设用户一开始就按下Enter键,那么程序读到的首个字符就是换行符。下面的代码处理这种情况:
  if (ch == ‘\n’)
  continue;

7.7.3 多重标签

  可以在switch语句中使用多重case标签。
vowels.c程序:

#include 
int main(void)
{
	char ch;
	int a_ct, e_ct, i_ct, o_ct, u_ct;
	a_ct = e_ct = i_ct = o_ct = u_ct = 0;
	printf("Enter some text;enter # to quit.\n");
	while((ch = getchar()) != '#')
	{
		switch(ch)
		{
			case 'a':
			case 'A': a_ct++;
					  break;
					  
			case 'e':
			case 'E': e_ct++;
					  break;
					  
			case 'i':
			case 'I': i_ct++;
					  break;
					  
			case 'o':
			case 'O': o_ct++;
					  break;
					  
			case 'u':
			case 'U': u_ct++;
					  break;
			
			default:  break;		  
		}				  // switch 结束 
	}				      // while 循坏结束
	printf("number of vowels:	A    E    I    O    U\n");
	printf("		     %4d %4d %4d %4d %4d\n", a_ct++, e_ct++, i_ct++, o_ct++, u_ct++);
	
	return 0;
 } 

运行结果:
在这里插入图片描述
程序说明:
  假设如果ch是字母 i,switch语句会定位到标签为case ‘i’ :的位置。由于该标签没有关联break语句,所以程序流直接执行下一条语句,即 i_ct++;。如果 ch 是字母 I,程序流会直接定位到case ‘I’ :。本质上,两个标签都指的是相同的语句。

7.7.4 switch 和 if else

  何时使用switch?何时使用if else?如果是根据浮点类型的变量或表达式来选择,就无法使用 switch。如果根据变量在某范围内决定程序流的去向,使用 switch 就很麻烦,这种情况 用 if 就很方便。

7.8 goto 语句

  goto语句有两部分:goto和标签名。标签的命名遵循变量命名规则,如下所示:
  goto part2;
  要让这条语句正常工作,函数还必须包含另一条标为part2的语句,该语句以标签名后紧跟一个冒号开始:
  part2: printf(“Refined analysis:\n”);

7.8.1 避免使用 goto

  胡乱跳转至程序的不同部分。简而言之,不要这样做!

  C程序员可以接受一种goto的用法——出现问题时从一组嵌套循环中跳出(一条break语句只能跳出当前循环):
  while (funct > 0)
  {
    for (i = 1, i <= 100; i++)
    {
      for (j = 1; j <= 50; j++)
      {
        其他语句
        if (问题)
          goto help;
        其他语句
      }
      其他语句
    }
    其他语句
  }
  其他语句
  help: 语句

  从其他例子中也能看出,程序中使用其他形式比使用goto的条理更清晰。

7.9 关键概念

  小结:程序跳转
  关键字:break、continue、goto
  一般注解:这3种语句都能使程序流从程序的一处跳转至另一处。

  1. break语句:
  所有的循环和switch语句都可以使用break语句。它使程序控制跳出当前循环或switch语句的剩余部分,并继续执行跟在循环或switch后面的语句。

  2. continue语句:
  所有的循环都可以使用continue语句,但是switch语句不行。continue语句使程序控制跳出循环的剩余部分。对于while或for循环,程序执行到continue语句后会开始进入下一轮迭代。对于do while循环,对出口条件求值后,如有必要会进入下一轮迭代。

  3. goto语句:
  goto语句使程序控制跳转至相应标签语句。冒号用于分隔标签和标签语句。标签名遵循变量命名规则。标签语句可以出现在goto的前面或后面。
  形式:
  goto label ;
  label : statement

7.10 本章小结

  1. if 语句使用测试条件控制程序是否执行测试条件后面的一条简单语句或复合语句。
  2. if else 语句可用于二选一的情况。
  3. else if,可构造多选一的结构。
  4. 在多数情况下,用条件运算符(?:)写成的表达式比 if else 语句更简洁。
  5. ctype.h系列的字符函数(如,issapce()和isalpha())为创建以分类字符为基础的测试表达式提供了便捷的工具。
  6. switch 语句可以在一系列以整数作为标签的语句中进行选择。如果紧跟在 switch 关键字后的测试条件的整数值与某标签匹配,程序就转至执行匹配的标签语句,然后在遇到break之前,继续执行标签语句后面的语句。

你可能感兴趣的:(C Primer Plus 第七章 C控制语句:分支和跳转 阅读笔记)