在经过了循环的学习之后,对C语言的熟悉更进一步了,为了更好的使用C程序来解决复杂的问题,就需要一些方法来控制和组织程序了。除了循环之外,我们还有分支结构(if 和 switch)让程序根据测试条件去执行相应的行为。

if 语句

先从一个简单的例子来进行学习if 语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
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;
}

这个程序可以实现的是读取一列数据,统计总天数和最低温度在0摄氏度以下的天数占总天数的百分比。

实现效果:

其中while循环的测试条件是之前学习过的利用scanf()的返回值来结束循环,因为temperature 的类型是float ,所以可以接受小数。

if 语句被称为分支语句或选择语句,因为它像一个交叉点,要在两条分支里选择一条执行,通用格式:

1
2
if (expression)
statement;

如果对expression 为真,则执行 statement 的内容,if 语句的结构和while 语句的结构很相似,主要的区别是若是条件满足,if 语句只能执行一次,而while 语句可以执行很多次。statement 部分可以是一条简单语句或是用花括号的复合语句。

if else 语句

简单形式的if 语句可以让程序选择执行一条语句,或者跳过这条语句,除了这个形式C 还提供了if else 形式,可以在两条语句中做出选择。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
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");

//可以修改为以下形式

if (all_days != 0)
printf("%d days total: % .1f%% were below freezing.\n", all_days, 100.0 * (float)cold_days / all_days);

else
printf("No data entered!\n");

这就将两个if 语句合并成了一个 if else 语句了。

通用格式:

1
2
3
4
if (expression)
statement1;
else
statement2;

Tip:如果要在if 和 else 之间执行多条语句,必须使用花括号把这些语句合成一个块变成复合语句,否则容易出现语法错误。

getchar()和putchar()

到目前为止,大部分的程序都需要输入数值,那么输入字符除了使用scanf()和printf()根据%c 转换说明读写字符,还有没有别的方法。

在C语言中有字符输入/输出函数:getchar()和putchar()。

getchar()函数不带任何参数,它从输入队列中返回下一个字符。

实例:

1
2
3
ch = getchar();
//效果与下面语句一样
scanf("%c", &ch);

而putchar()函数是打印它的参数

实例:

1
2
3
putchar(ch);
//效果与下面语句一样
printf("%c", ch);

由于这些函数只处理字符,所以他们比printf()和scanf()函数更快更简洁,而且这两个函数是不需要转换说明的,因为它们只需要处理字符,通常定义在stdio.h的头文件里。

下面我们在具体的程序中去使用这两个函数

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#define 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;
}

这段程序就是将每个非空格都替换成ASCII 表中的下一个字符,空格保持不变的效果。

这个程序总体来说还是有可以改进的地方,例如可以将while循环的测试条件中加入获取字符这一项可以节省一些步骤,也能使得每个行为更加的清晰。但是在细节方面也需要注意,像是 (ch = getchar()) 的两个圆括号就不能遗忘,还有逻辑符号的先后顺序等。

ctype.h 系列的字符函数

在上面这个程序也还是有漏洞,因为它只是针对字母和非字母字符,但是非字母字符中也有许多的种类,像是标点符号等就显得没有这么方便转换,而且转换之后也不是我们想要的效果。其实C 语言有一系列专门处理字符的函数,ctype.h 的头文件里包含了这些函数的原型,利用这些函数可以对上面的程序进行优化。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char ch;

while ((ch = getchar()) != '\n')
{
if (isalpha(ch))
putchar(ch + 1);
else
putchar(ch);
}
putchar(ch);

return 0;
}

其中,isalpha()函数是拿来判断是否是字符的,非字符为0,小写为2,大写为1。这一段改进之后的程序相较于之前就好了很多,很多行为也更加的清晰。

在ctype.h 这个头文件中还有许多针对字符的测试函数:

多重选择 else if

在现实生活中一般会出现不止一种选择,在程序中我们可以使用else if 来应对这种情况,来扩展if else 结构中不足的部分,最简单的一个就是阶梯电费的收费,这就是一个多重选择模型,因为它会在不同的用电区间里实行差额电价。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#define RATE1 0.13230
#define RATE2 0.15040
#define RATE3 0.30025
#define RATE4 0.34025
#define BREAK1 360.0
#define BREAK2 468.0
#define BREAK3 720.0
#define BASE1 (RATE1 * BREAK1)
#define BASE2 (BASE1 + ((BREAK2 - BREAK1) * RATE2))
#define BASE3 (BASE1 + BASE2 + ((BREAK3 - BREAK2) * RATE3))
int main(void)
{
double kwh;
double bill;

printf("Please enter the kwh used.\n");
scanf("%lf", &kwh);
if (kwh <= BREAK1)
bill = kwh * RATE1;
else if (kwh <= BREAK2)
bill = BASE1 + ((kwh - BREAK1) * RATE2);
else if (kwh <= BREAK3)
bill = BASE2 + ((kwh - BREAK2) * RATE3);
else
bill = BASE3 + ((kwh - BREAK3) * RATE4);

printf("The charge for %.lf kwh is $%1.2f.\n", kwh, bill);

return 0;
}

这个程序就完整的把阶梯电费的功能给实现出来了,若是需要修改,我们仅仅需要在符号常量更改费率和费率临界点即可。

实际上,else if 是上面 if else 语句的变式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (kwh <= BREAK1)
bill = kwh * RATE1;
else if (kwh <= BREAK2)
bill = BASE1 + ((kwh - BREAK1) * RATE2);
else if (kwh <= BREAK3)
bill = BASE2 + ((kwh - BREAK2) * RATE3);
else
bill = BASE3 + ((kwh - BREAK3) * RATE4);
//实际效果等同于下面
if (kwh <= BREAK1)
bill = kwh * RATE1;
else
if (kwh <= BREAK2)
bill = BASE1 + ((kwh - BREAK1) * RATE2);
else
if (kwh <= BREAK3)
bill = BASE2 + ((kwh - BREAK2) * RATE3);
else
bill = BASE3 + ((kwh - BREAK3) * RATE4);

上面这两种形式是完全等价的,效果也是完全一样的,实际就是一种嵌套语句。切记这种形式的最后一定是单独的一个else ,但是也不是能一直嵌套的,在C99标准中,最少支持127层嵌套。

else 与 if 配对

若是在一个程序中有许多的if 和 else ,编译器怎么知道if 对应那个else 呢。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
if (number > 6)
if (number < 12)
printf("You're close!\n");
else
printf("Sorry, you lose a turn!\n");
//这只是其中一种情况
if (number > 6)
{
if (number < 12)
printf("You're close!\n");
}
else
printf("Sorry,you lose a turn!\n");

具体匹配规则:如果没有花括号,else 会与离它最近的if 匹配,除非最近的if 被花括号括起来了,像是实例中的第二部分一样。

多层嵌套的if 语句

上面我们介绍的 if else 序列是嵌套 if 的一种形式,从一系列的选项中选择一个执行,在选择一个特定选项之后又引出其他选择,又可以使用另外一个选择。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <stdbool.h>
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;
}

上面这是一个输入一个数字,判断是否是一个素数,若不是则找出其约数对并显示的程序。

小结:

上面主要学习了使用if 语句进行选择,关键字:if else 。

一般来说,if else 所组成的语句总共有三种形式:

形式1:

if (expression)

​ statement

形式2:

if(expression)

​ statement1

else

​ statement2

形式3:

if (expression1)

​ statement1

else if (expression2)

​ statement2

else

​ statement3

逻辑运算符

经过上面的学习,对于if 语句和 while 语句通常使用关系表达式作为测试条件比较熟练了。 接下来学习逻辑运算符的使用。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#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;
}

这个程序是计算输入的一行句子中除了单双引号以外其他字符的数量,且使用句号(.)作为句子结尾。

这段程序中最重要的就是if 语句的测试条件中使用了逻辑与运算符&&,这句话翻译成文字就是测试的字符不是双引号且不是单引号,那么字符数增加1。逻辑运算符两侧均为真,整个表达式才为真,但是逻辑运算符的优先级比关系运算符低,所以可以不用加圆括号。(最好加上!)

三种逻辑运算符:

剩下的这些就是逻辑的基本运算,难度较低不再赘述。

iso646.h头文件(备选逻辑运算符)

一般来说,C 语言是用美式键盘开发的语言,但是世界各地的键盘不尽相同,因此在C99标准中新增了可以替代逻辑运算符的拼写,这些存在于 iso646.h头文件里,在这里面,and 替代 &&、or 替代||、not 替代!

实例:

1
2
3
4
5
if (ch != '"' && ch != '\'')
charcount++;
//可以改写成以下的形式
if (ch != '"' and ch != '\'')
charcount++;

但是为什么不直接使用对应的拼写呢,这是由于C 语言一直要坚持保持较少的关键字。

优先级

在这几个逻辑运算符中,! 运算符的优先级很高,比乘法运算符的还要高,和递增运算符的优先级相同,只比圆括号的优先级低。&&运算符的优先级比 || 运算符要高,但是两者都比关系运算符要低,比赋值运算符要高。

Tip:在实际应用中,还是可以更多的使用圆括号,这样即使忘记了运算符的优先级,表达式的含义也会非常的清楚。

求值顺序

除了两个运算符共同使用一个运算对象的情况外,C 语言一般不保证对复杂表达式的那一部分进行求值,但是逻辑运算符是其中的例外,C 语言保证逻辑表达式的求值顺序是从左往右,&& 和 || 都是序列点,从一个运算对象到下一个运算对象,所有的副作用都会产生效果,而且C 一旦发现某一个元素使得表达式无效,会立即停止求值。

1
2
while ((c = getchar()) != ' ' && c != '\n')
//所以这句话可以完美执行,不会导致c在还没赋值之前就和\n进行比较。

小结:逻辑运算符的运算对象一般是关系表达式,!运算符只需要一个运算对象,其他两个都是需要两个运算对象,左右各一个。求值顺序记住是从左往右即可!

范围

&&运算符可用于测试范围。假设要测试score是否在90-100之间,可以这样写:

1
2
3
4
5
if(range >= 90 && range <= 100)
printf("Good show!\n");
//记住不要模仿数学上的写法变成
if(90 <= range <= 100)
printf("Good show!\n"); //不好的形式

若是按照第二种形式来写代码,会出现语义错误,因为 <= 运算符的求值顺序是从左往右,所以编译器会将其解释成((90 <= range) <= 100),这样就不是我们的本意了,因此在范围测试中都使用 && 。

一般的使用中都是集中在判断一个字符是否是一个小写字母:

1
2
if (ch >= 'a' && ch <= 'z')
printf("That's a lowercase character.\n");

但是这种方法对ASCII码的字符比较有效果,若是编码系统是EBCDIC就没用了,对应的可移植方法就是使用ctype.h 中的 islower()函数。因为无论是那种编码系统,islower ()函数都能正常运行。

统计单词的程序

经过上面的学习之后,我们可以开始使用所学来编写一个统计单词数量的程序。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>
#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;
printf("Please 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;
}

在这其中,!inword 和 inword == false 等价。上面的整个测试条件比单独判断每个空白字符的可读性要高许多。若是c 不是空白字符,且如果c 不在单词里,那么一定是一个单词的开头,所以单词数要加1。等读到下一个空白字符时,inword 又会被置成false,但是若是单词中间有多个空格时,程序可能会出现错误,在后面的学习中会说明如何解决。

条件运算符:?:

C 语言提供条件表达式作为表达if else 语句的一种便捷方式,那么怎么使用这个条件表达式呢,这个运算符分为两个部分,需要三个运算对象,这是C 语言中唯一一个三元运算符。

实例:

1
2
3
4
5
6
7
8
x = (y < 0) ? -y: y;
//这行代码的意思就是当y < 0时,x = -y,反之x = y。
//转换成if else的形式如下:
if (y < 0)
x = -y;
else
x = y;
//相较于if else的形式简单了不少。

条件表达式的通用形式如下:

1
2
3
4
expression1 ?expression2 :expression3
//最简单的例子
max = (a > b) ? a : b;
//把两个值中的最大值赋给变量。

其实条件运算符能完成的事情都可以通过if else语句完成。但是使用条件运算符能使得代码更加的简洁。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#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;
}

这个程序用于计算刷给定面积的墙面需要多少桶油漆,其中主要用到的是取整和取余的方法。还有一个是根据单复数来打印是can还是cans。

循环辅助:continue 和 break

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

continue语句

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

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
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("Please 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;
}
printf("Accepting %0.1f:\n", score);
min = (score < min) ? score : min;
max = (score > max) ? score : max;
total += score;
n++;
printf("Enter the 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 vaild scores were entered.\n");
return 0;
}

在这个程序中,while 语句持续读取输入,直至用户输入非数值数据,循环中的if 语句会筛选出无效的数据。continue 语句让程序跳过了处理有效数字的部分使得程序进入了下一个循环。

其实我们有两种方法可以不去使用continue,一是省略continue,把剩余部分放在一个else块中;而另外一种就是换一个格式来代替:

1
2
3
if (score < MIN || score > MAX)
//可以换成以下的形式
if (score >= MIN && score <= MAX)

但是在这种情况下,使用continue 的好处就是可以减少语句组的一级缩进。当语句很长或者嵌套较多时,简洁的格式提高了代码的可读性。

continue 有时候还可以作为占位符来使用,如下所示:

1
2
3
4
5
6
while (getchar() != '\n')
;
//可以转换成另外一种形式
while (getchar() != '\n')
continue;
//在加入continue之后使得可读性更高了

若是使用了continue 使得代码更加复杂了就不要使用continue 了,一般来说,把if 的测试条件反过来就可以避免使用continue了。上面是介绍了continue 语句让程序跳过循环的余下部分,那么从何处继续开始循环呢。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
count = 0
while (count < 10)
{
ch = getchar();
if (ch == '\n')
continue;
putchar(ch);
count++;
}
//这个循环是读取10个字符(除换行符外),并显示它们。
//对于for循环来说,执行continue后的下一个行为是对更新表达式求值。
for (count = 0; count < 10; count++)
{
ch = getchar();
if (ch == '\n')
continue;
putchar(ch);
}
//由于在continue之后,count进行了递增,所以和上一个例子不同,for循环中会显示换行符,仅仅只是读取10字符。

break语句

程序执行到循环的break语句时,会终止包含它的循环,并继续执行下一阶段。若是break语句位于嵌套循环内,它只会影响当前的循环。

下图生动的说明了continue和break语句的区别:

break 语句还可以用于因其他原因退出循环的情况。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
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 the width.\n");

if(scanf("%f", &width) != 1);
break;

printf("Width = %0.2f:\n", width);
printf("Area = %0.2f", length * width);
printf("Enter the length of the rectangle:\n");
}
printf("Done!\n");
return 0;
}

这个程序是根据输入的长和宽来计算面积的一个程序,若是输入一个非数字则终止循环。

但其实可以将循环中的测试表达式进行简洁,但是为了更好的显示用户即时输入的值,还是使用上面的更加方便。

1
2
3
4
while (scanf("%f", &length) == 1)
//可以更改成下面的形式
while (scanf("%f %f", &length, &width) == 2)
//这样可以直接进入我们操作的部分,但是不会进行输入值的显示

和continue 一样,如果使用了break 之后代码反而复杂了,就不要使用了。

1
2
3
4
5
6
7
8
9
while ((ch = getchar()) != '\n')
{
if (ch == '\t')
break;
putchar(ch);
}
//可以换成更好的一段,使用break就没有这么好了。
while ((ch = getchar()) != '\n' && ch != '\t')
putchar(ch);

break 语句对于接下来的switch 语句非常重要,在for循环中的break 和 continue 完全不一样,执行break 语句会直接执行循环后面的第一条语句,连更新部分都会跳过。嵌套循环的break 只会跳出当前循环,若是要跳出外层循环还要一个break。

多重选择:switch 和 break

我们在使用条件运算符和if else 语句时很容易编写二选一,但是有时候程序需要在多个选项中进行选择。虽然也可以使用 if else if …… else来完成,但是大多数的情况下使用switch 语句更加方便。下面的实例可以展示怎么使用switch 语句,输入一个字母,然后打印和该字母相同的动物名称。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char ch;
printf("Give me a letter of the alphabet, and I will give");
printf("an animal name\n begining 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");
}
else
printf("I only need lowercase letter.\n");
while (getchar() != '\n')
continue;
printf("Please type in a letter; type # to end my act.\n");
}
printf("Bye!\n");
return 0;
}

上面这个程序就是展示了switch的用法,但是长度有限,我们的字母只能展示到 f 后面的字符依次类推。

使用效果:

下面来分析一下switch的具体原理。

switch语句

主要是对跟在关键字 switch 后圆括号中的表达式求值,然后对标签列表进行扫描,直到发现一个匹配值为止,然后程序会跳到所在行,若是没有匹配的标签,有default 标签行,就跳转至该行否则就执行switch 之后的语句。

break 语句在其中主要起到什么作用呢,它会让程序离开switch语句,跳转至switch语句后面的下一条语句。若是没有break,就会从匹配标签开始执行一直到末尾。

Tip:break 语句可以用于循环和switch 语句中,但是continue 只能用于循环,若是switch 语句在一个循环中,continue 便可以作为switch 语句的一部分,像在其他循环中一样,continue 让程序跳出剩余部分。

switch 语句的圆括号中的测试表达式的值应该是一个整数值(包括char类型)。case 标签必须是整数类型(包括char 类型)的常量,或者是常量表达式。切记不能使用变量作为case 标签,switch结构如下:

1
2
3
4
5
6
7
8
9
switch (整型表达式)
{
case 常量1
语句
case 常量2
语句
default
语句
}

仅读每行首字符

在上面的程序运行过程中,还有一个独到的地方,当我们输入多个字母时,只会处理第一个字符,这类丢弃一行中其他字符的行为,会经常出现在响应单字符的交互程序中,实现的代码也比较的简单:

1
2
while (getchar() != '\n')
continue;

循环从输入中读取字符包括按下Enter键所产生的换行符。**但是函数的返回值并没有赋值给ch,由于最后丢弃的是换行符,所以下一个读取的是下一行的首字母,在最外层的循环中,getchar()的值被赋值给了ch。

若是用户一开始就按下Enter键的话,那么程序第一个读到的就是换行符,但是这就不符合接下来输入字母的要求了。可以使用一段代码来解决:

1
2
if ('\n' == ch)
continue;

这样就成功避免了用户在一开始就输入换行符的情况。

多重标签

在上面的程序中,都是一个标签对应一个语句或是一种情况,那若是我们需要多个标签都是对应同一个语句又该如何去做呢。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
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;
}
}
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;
}

这个程序就是多个标签对应同一个输出,不过这只是看上去如此,前面说过若是没有break,那么会继续往下执行,所以看起来像是两个标签对应一个输出,底层的逻辑从来没变过。但是如果使用 ctype.h 头文件中的 toupper()函数就可以避免这个多重选择,在测试之前就可以把小写字母转换成大写字母。

小结:

关键字:switch

程序根据测试表达式的值跳转至相应的case标签处。然后执行其中的语句,除非执行到了break 语句才会进行重定向。**尤为重要的是测试表达式和case 标签都必须是整数值,标签必须是常量或者是常量组成的表达式,若是没有标签进行匹配,则转到default 的语句(如果有),否则就是直接执行switch 语句后面的语句。

switch 和if else

对于什么时候使用 switch 和 if else ,经常会有疑惑,但是数据是浮点型的话就无法使用switch了,根据变量在某一个范围内决定程序流,使用switch也是不方便的,这样的情况还是采用 if 来的更好。

实例:

1
2
if (integer < 1000 && integer > 2)
//这个若是用switch,需要将每个整数都设置case标签

但是使用switch 也有好处,程序通常会运行的快一点,生成的代码少一点。

goto语句

早期版本的BASIC语言依赖的goto语句,在C 语言中依然可以使用,但是和前期的语言有所不同,没有goto 语句的C程序也能良好运行,而且goto 语句容易被滥用。一般来说,goto 语句通常不需要它。

goto语句有两部分:goto 和标签名;标签命名遵循变量命名规则:

1
2
3
goto part;
part: printf("Refined analysis:\n");
//要让这条语句正常工作,还要在后面加入part的内容,标签名后面跟上冒号

避免使用goto语句

在原则上,根本不需要在C程序中使用goto语句,先来看一下goto语句的常见情况,然后再来介绍C 语言的解决方案:

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
if (size < 12)
goto a;
goto b;
a: cost = cost * 1.5;
flag = 2;
b: bill = cost * flag;
//若是要转化成C语言的形式如下
if (size < 12)
{
cost = cost * 1.5;
flag = 2;
}
bill = cost * flag;

上面主要演示了使用goto语句和C 语言之间的转换。

二选一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (ibex > 14)
goto a;
sheds = 2;
goto b;
a: sheds = 3;
b: help = 2 * sheds;
//下面是转换到C语言
if (ibex > 14)
sheds = 3;
else
{
sheds = 2;
help = 2 * sheds;
}

实际上,新版的BASIC语言已经把else纳入新的语法中了。

创建不确定的循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
readin: scanf("%d", &score);
if (score < 0)
goto stage2;
lots of statements
goto readin;
stage2: more stuff;
//上面的程序可以使用while语句来替代
scanf("%d", &score);
while (score >= 0)
{
lots of statement;
scanf("%d", &score)
}
more stuff;

上面就演示了不确定循环在goto 语句和C 语言的while循环之间的转换。

跳出循环,C语言使用break语句,实际上,break和continue是goto的特殊形式,使用它们的好处是其名称已经表明了它们的用法,这些语句不用标签,不会有标签放错位置的烦恼。

在C 语言中可以接受一种goto的用法,那就是出现问题时,从一组嵌套循环中跳出(一个break只能跳出当前循环)。

小结:程序跳转

关键字:continue、break、goto

注解:这3种语句都能使程序流从程序的一处跳转到另外一处。

break语句:

所有的循环和switch语句都可以使用break语句。

continue语句:

所有的循环都可以使用continue语句,但是switch语句不行。对于while和for循环,程序执行到continue语句就会开始进入下一轮迭代,对于do while循环,对出口条件求值后,看情况进入循环。

goto语句:

goto语句使得程序控制跳转到相对应的标签语句。冒号用于分隔标签和标签语句。标签语句可以出现在goto前面或者后面(或许就是由于这样的自由度,所以在C 语言里不受待见。)

关键概念

智能的一个方面就是根据情况做出反应,所以选择语句是开发具有智能行为程序的基础,C 语言可以if 、if else和switch语句,以及条件运算符(?:)可以实现智能选择。

if 和 if else 语句使用测试条件来判断执行那些语句,所有的非零值都会被视为 true,零被视为 false,测试一般涉及关系表达式、逻辑表达式。

我们需要记住一个通用的原则,如果需要测试两个条件,应该使用逻辑运算符把两个测试表达式组合起来,而不是直接合并。

实例:

1
2
3
4
5
if (a < x < z)	//这是错误的写法
if (ch != 'q' && != 'Q') //这也是错误的写法
//正确的写法如下
if (x > a && x < z)
if (ch != 'q' && ch != 'Q')

那么关于分支和跳转的部分就叙述到这里,之后我们将学习字符的输入/输出以及输入验证。