開發與維運

带你读《C编程技巧:117个问题解决方案示例》之二:控制语句

点击查看第一章
点击查看第三章

第2章

控制语句
本章介绍利用控制语句的功能来解决问题的方法。C包含种类丰富的控制语句。C中的控制语句大致可分为三类。
□选择语句:选择语句用于在几个计算机控制流中选择其一。选择语句有两种:if-else和switch。
□迭代语句:迭代语句用于有限次地重复执行一组语句。迭代语句有三种:while、do-while和for。
□跳转语句:跳转语句有四种,即break、continue、goto和return。通常,计算机控制从源代码中前面的语句顺序流到下一个语句。当你需要绕过此顺序流,并让计算机控制从一个语句跳转到另一个语句(不一定是连续语句)时,可以使用跳转语句。
goto语句用于跳转到同一函数中的另一个语句。continue语句仅用于迭代语句中。break语句仅用于迭代或switch语句中。return语句用于函数中。




2.1 求1到N的整数的总和

问题
你希望开发一个以交互方式计算1到N的整数的总和的程序。
解决方案
编写一个C程序,使用以下规格说明计算1到N的整数的总和:
□程序使用for循环执行1到N的整数的总和。for循环没有什么特殊的,你也可以使用while循环或do-while循环,但在这些类型的程序中,for循环是优选的。
□程序要求用户输入数字N(0□当计算的总和显示在屏幕上时,程序会询问用户是否要计算另一个总和或退出。
代码
以下是使用这些规格说明编写的C程序的代码。在文本编辑器中键入以下C程序并将其保存在C:Code文件夹下名为sum.c的文件中:






image.png

编译并执行此程序。此程序的一次运行结果如下所示:

image.png

工作原理
包含在LOC 16~18中的for循环执行1~N的整数的求和。在此程序中使用具有两层嵌套的do-while循环。只要用户未能在指定范围内输入数字N,内部do-while循环就会将用户保持在循环内。只要用户想要再次执行求和,外部do-while循环就会将用户保持在循环内。内部do-while循环增加了此程序的稳健性。
除了for循环之外,还可以使用while或do-while循环来执行求和。要使用while循环执行求和,请使用以下代码行替换LOC 16~18:


image.png

要使用do-while循环执行求和,请将LOC 16~18替换为以下代码行:

image.png

编写循环的终止条件时要小心。轻率地编写循环的终止条件是bug的发源地。
■注意 bug围绕边界值“游荡”。
例如,查看此处给出的for循环:


image.png

乍一看,你可能认为这个for循环执行100次迭代,但实际上,它只执行99次迭代。因此,在处理边界值时要小心。
源代码中的错误是bug。发现和纠正源代码中的错误的过程称为调试(debug)。
编程专家能够创建具有最少数量的可能bug的程序,并且还知道如何调试程序。编写一个完全没有bug的小程序是可能的,但是由数千代码行组成的专业程序永远不会没有bug。

2.2 计算数字的阶乘

问题
你想要开发一个程序来计算数字的阶乘。
解决方案
正整数n的阶乘由n!表示,它的定义如下:
n! = 1×2×…×n
这里给出了一些数字的阶乘:
0! = 1(根据定义)
1! = 1
2! = 1×2 = 2
3! = 1×2×3 = 6
编写具有以下规格说明的C程序:
□程序使用for循环计算N的阶乘。
□程序要求用户输入数字N(0<N≤12)。如果用户输入该范围之外的数字N,则程序要求用户重新输入数字。
□当计算的阶乘显示在屏幕上时,程序会询问用户是否想要计算另一个阶乘或退出。
代码
以下是使用这些规格说明编写的C程序的代码。在文本编辑器中键入以下C程序并将其保存在文件夹C:Code中,文件名为fact.c:















image.png
image.png

编译并执行此程序。此程序的一次运行结果如下所示:

image.png

工作原理
LOC 16~18中包含的for循环计算数N的阶乘。在此程序中使用具有两层嵌套的do-while循环。只要用户未能在指定范围内输入数字N,内部do-while循环就会将用户保持在循环内。只要用户想要再次计算阶乘,外部do-while循环就会将用户保持在循环内。内部do-while循环增加了此程序的稳健性。除了for循环之外,还可以使用while或do-while循环来计算数字N的阶乘。

2.3 生成斐波那契数列

问题
你想开发一个程序来生成斐波那契数列。
解决方案
Leonardo Fibonacci(1180—1250),也被称为比萨的莱昂纳多,是一位意大利数学家。他撰写了许多关于数学的优秀论文,如“Liber Abaci”“Practica Geometriae”“Flos”和“Liber Quadratorum”。斐波那契数列以其发明人命名并在“Liber Abaci”中提及,从0和1开始,每个连续项都是前两个项的和。根据定义,第一项为0,第二项为1。前几项列于此处:
第一项 根据定义 0
第二项 根据定义 1
第三项 0 + 1 = 1
第四项 1 + 1 = 2
第五项 1 + 2 = 3
第六项 2 + 3 = 5
斐波那契数列中的项也称为斐波那契数。这里给出了一个可以生成斐波那契数的例程的伪代码:
image.png










编写具有以下规格说明的C程序:
□程序使用for循环计算斐波那契数。
□程序要求用户输入数字N(0<N≤45)。如果用户在此范围之外输入数字N,则程序会要求用户重新输入此数字。然后程序生成N个斐波那契数。
□当计算出的斐波那契数显示在屏幕上时,程序会询问用户是否想要计算另一个斐波那契数或退出。
代码
以下是使用这些规格说明编写的C程序的代码。在C文件中键入以下文本(程序)并将其保存在文件夹C:Code下名为fibona.c的文件中:




image.png
image.png

编译并执行此程序。此程序的一次运行结果如下所示:

image.png

工作原理
LOC 18~27中包含的for循环完成了大部分工作。LOC 23~26中包含的代码计算斐波那契数。LOC 19和LOC 21中包含的代码在屏幕上显示计算出的斐波那契数。在此程序中使用具有两层嵌套的do-while循环。只要用户未能在指定范围内输入数字N,内部do-while循环就会将用户保持在循环内。只要用户想要再次计算斐波那契数,外部do-while循环就会将用户保持在循环内。内部do-while循环增加了此程序的稳健性。除了for循环之外,还可以使用while或do-while循环来计算斐波那契数。斐波那契数列在植物学、电网理论、搜索和排序中有应用。

2.4 确定给定数字是否为质数

问题
你希望开发一个程序来确定给定的数字是否为质数。
解决方案
质数是一个正整数,只能被1和它本身整除。前几个质数如下:2, 3, 5, 7, 11, 13, 17, 19。除了2之外,所有质数都是奇数。你将开发一个程序来确定给定的数字是否为质数。
程序执行开始时,系统会要求你输入2~2 000 000 000范围内的数字。键入此范围内的任何整数,程序将告诉你此数字是否为质数。另外,输入0以终止程序。显然,要确定数字N是否为质数,必须将它除以2~(N-1)之间的所有数字并检查余数。如果每个除法的余数都不为零,则数N是质数,否则,它不是质数。但是,实际上可以将N的数字除以2~√N(N的平方根)之间的所有数字并检查余数。如果N不能被2~√N之间的任何数字完全整除,那么它肯定不能被2~(N-1)之间的任何数字整除。
此处给出了一个例程,用于确定给定数字lngN是否为质数。这里,isPrime是一个int变量,lngN、lngM和i是long int变量,lngN的值为3或更大,并且isPrime设置为1(被解释为真)。





image.png

在此例程的LOC 2中,通过隐式类型转换将lngN的值转换为double类型,然后将其传入sqrt()以计算其平方根。sqrt()返回的结果被传到ceil()以将其转换为较大的最接近的整数。在隐式类型转换后,ceil()返回的结果将赋值给lngM。
接下来,lngN将除以2到lngM之间的所有数字。如果在所有这些除法中余数都为非零,那么lngN是质数,否则不是。这是在LOC 3~8的for循环中完成的。实际除法在LOC 4中执行,并检查余数的值(是否为零)。如果余数为零,则执行LOC 5和6。在LOC 5中,int变量isPrime的值设置为零。在LOC 6中,执行break语句以终止for循环。注意isPrime的值,结果显示在屏幕上。如果isPrime为1(真),则lngN为质数;如果isPrime为0(假),则lngN不是质数。
编写具有以下规格说明的C程序:
□程序使用for循环检查数字的素性。
□程序要求用户输入数字N(2≤N≤2 000 000 000),以确定此数字是否为质数。如果用户输入该范围之外的数字N,则程序要求用户重新输入此数字。然后程序检查此数字的素性。如果用户输入0,则程序终止。
代码
以下是使用这些规格说明编写的C程序的代码。在C文件中键入以下文本(程序)并将其保存在文件夹C:Code中,文件名为prime.c:






image.png

编译并执行此程序。此程序的一次运行结果如下所示:

image.png

工作原理
LOC 25~30中包含的for循环完成了检查数字的素性的大部分工作。LOC 31~34中的代码显示结果。在此程序中使用具有两层嵌套的do-while循环。只要用户未能在指定范围内输入数字N,内部do-while循环就会将用户保持在循环内。只要用户想要检查新数字的素性,外部do-while循环就会将用户保持在循环内。内部do-while循环增加了此程序的稳健性。请注意LOC 35,此处摘录以供你快速参考:

image.png

这似乎是一个无限循环,因为括号中没有比较语句。但是,在LOC 18中提供了终止循环的规定,这里也摘录以供你快速参考:

image.png

当lngN的值为零时,此循环的执行将成功终止。
库函数ceil()和sqrt()在LOC 24中使用,这里也摘录以供你快速参考:

image.png

库函数ceil()和sqrt()是数学函数,这就是通过LOC 2将头文件math.h包含在这个程序中的原因。术语sqrt代表“平方根”,术语ceil代表“上取整”,这反过来意味着上限。以下是使用库函数sqrt()的语句的通用语法:

image.png

这里,dblY是一个表达式,其计算结果为double类型的常量,而dblX是double类型的变量。函数sqrt()计算dblY的平方根并返回结果,该结果赋值给变量dblX。
函数ceil()将double值(作为参数传递)转换为较大的最接近的整数值并返回结果。以下是使用函数ceil()的语句的通用语法:

image.png

这里,dblY是一个表达式,其计算结果为double类型的常量,而dblX是一个double变量。

2.5 计算正弦函数

问题
你想使用无穷级数展开计算角度x的正弦值。
解决方案
这里给出了无穷级数展开的公式:
sin x = x - x3 / 3! + x5 / 5! - x7 / 7! + …
这里,x是以弧度表示的角度,它的取值范围为-1≤x≤1。可以看到连续项的值持续迅速减小。因此,仅包括前十项就足够了。如果x的值为1,则第十项的贡献约为2E-20,而第40项的贡献约为1.7E-121。
编写具有以下规格说明的C程序:
□程序使用for循环计算角度x的正弦值。
□程序要求用户输入角度x(-1≤x≤1)。如果用户在此范围之外输入角度x,则程序会要求用户重新输入。
□当屏幕上显示角度x的正弦值时,程序会询问用户是否要计算另一个角度的正弦值或退出。
代码
以下是使用这些规格说明编写的C程序的代码。在C文件中键入以下文本(程序)并将其保存在文件夹C:Code中,文件名为sine.c:











image.png
image.png

编译并执行此程序。此程序的一次运行结果如下所示:

image.png

工作原理
LOC 19~23中包含的for循环计算角度x的正弦值。LOC 24中的代码显示结果。在此程序中使用具有两层嵌套的do-while循环。只要用户未能在指定范围内输入角度x,内部do-while循环就会将用户保持在循环内。只要用户想要计算另一个角度的正弦值,外部do-while循环就会将用户保持在循环内。内部do-while循环增加了此程序的稳健性。

2.6 计算余弦函数

问题
你想使用无穷级数展开计算角度x的余弦值。
解决方案
这里给出了无穷级数展开的公式:
cos x = 1 - x2 / 2! + x4 / 4! - x6 / 6! + …
这里,x是弧度,它的取值范围为-1≤x≤1。可以看到连续项的值持续迅速减小。因此,仅包括前十项就足够了,如前面所讨论的那样。
编写具有以下规格说明的C程序:
□程序使用for循环计算角度x的余弦值。
□程序要求用户输入角度x(-1≤x≤1)。如果用户在此范围之外输入角度x,则程序会要求用户重新输入。
□当屏幕上显示角度x的余弦值时,程序会询问用户是否要计算另一个角度的余弦值或退出。
代码
以下是使用这些规格说明编写的C程序的代码。但是,这次使用的编码算法与前面相比略有不同。在C文件中键入以下文本(程序)并将其保存在文件夹C:Code中,文件名为cosine.c:











image.png
image.png

编译并执行此程序。此程序的一次运行结果如下所示:

image.png

工作原理
两层嵌套的for循环用于计算角度x的余弦值。LOC 30显示结果。在此程序中使用具有两层嵌套的do-while循环。只要用户未能在指定范围内输入角度x,内部do-while循环就会将用户保持在循环内。只要用户想要计算另一个角度的余弦,外部do-while循环就会将用户保持在循环内。内部do-while循环增加了此程序的稳健性。

2.7 计算二次方程的根

问题
你想要计算二次方程的根。
解决方案
你想要计算二次方程ax2 + bx + c = 0的根。这些根由以下公式给出:
(-b + √b2 - 4ac)/2a  和  (- b - √b2 - 4ac)/2a
根据a、b和c的值,根可能是实数或虚数。
编写具有以下规格说明的C程序:
□程序要求用户输入a、b和c的值,可以是整数或浮点数。
□程序使用前面给出的公式计算根并在屏幕上显示结果。
□当在屏幕上显示二次方程的根时,程序会询问用户是否想要计算另一个二次方程的根或退出。
代码
以下是使用这些规格说明编写的C程序的代码。在C文件中键入以下文本(程序)并将其保存在文件夹C:Code中,文件名为roots.c:











image.png

编译并执行此程序。此程序的一次运行结果如下所示:

image.png
image.png

工作原理
执行简单的数学运算以计算二次方程的根。取决于系数a、b和c的值,根可能是实数或虚数。因此,我们规定要测试根是实的还是虚的。只要用户想要计算另一个二次方程的根,do-while循环就会将用户保持在循环内。

2.8 计算整数的反转数

问题
你想要计算整数的反转数。
解决方案
例如,如果给定的整数是12345,那么它的反转数是54321。
编写具有以下规格说明的C程序:
□程序要求用户输入整数N(0<N≤30 000)。如果用户输入该范围之外的整数N,则程序要求用户重新输入整数。
□程序计算整数的反转数并在屏幕上显示结果。
□然后程序询问用户是否想要计算另一个整数的反转数或退出。
代码
以下是使用这些规格说明编写的C程序的代码。在C文件中键入以下文本(程序)并将其保存在文件夹C:Code中,文件名为reverse.c:









image.png
image.png

编译并执行此程序。此程序的一次运行结果如下所示:

image.png

工作原理
执行简单的数学运算以计算整数的反转数。在此程序中使用具有两层嵌套的do-while循环。只要用户输入的不是在指定范围内的整数N,内部do-while循环就会将用户保持在循环内。只要用户还想要计算另一个整数的反转数,外部do-while循环就会将用户保持在循环内。内部do-while循环增加了此程序的稳健性。

2.9 使用嵌套循环打印几何图案

问题
你希望使用嵌套循环(而不是使用5个printf()语句)在屏幕上生成和打印以下几何图案:

image.png

这个图案的秩是5,即它由5行组成。你希望生成从1到9的任何秩的图案。
解决方案
可以使用两层嵌套的for循环以编程方式打印此图案。编写具有以下规格说明的C程序:
□程序要求用户输入图案的秩(1≤N≤9)。如果用户输入该范围之外的N,则程序要求用户重新输入N。
□程序使用两层嵌套的for循环打印所需的图案。但是,此程序中将有4个for循环。
□然后程序询问用户是否要打印其他图案或退出。
代码
以下是使用这些规格说明编写的C程序的代码。在C文件中键入以下文本(程序)并将其保存在文件夹C:Code中,文件名为pattern.c:







image.png

编译并执行此程序。此程序的一次运行结果如下所示:

image.png

工作原理
for循环的正确组合生成所需的图案。在此程序中使用具有两层嵌套的do-while循环。只要用户输入的不是在指定范围内的整数N,内部do-while循环就会将用户保持在循环内。只要用户还想要生成不同秩的另一个图案,外部do-while循环就会将用户保持在循环内。内部do-while循环增加了此程序的稳健性。

2.10 生成终值利息系数表

问题
你希望生成一个终值利息系数表(FVIF)并将其打印在屏幕上。
解决方案
可以使用两层嵌套的for循环以编程方式生成和打印此表。编写具有以下规格说明的C程序:
□此程序生成FVIF表,利率从1%到6%不等,期限从1年到10年不等。
□FVIF值应精确到小数点后三位。
代码
以下是使用这些规格说明编写的C程序的代码。在C文件中键入以下文本(程序)并将其保存在文件夹C:Code中,文件名为interest.c:







image.png
image.png

编译并执行此程序。此程序的一次运行结果如下所示:

image.png

工作原理
FVIF表可用于计算货币的未来价值。n年后本金额(Po)的未来价值(FVn)(每年的利率为i%)由下列公式给出:

image.png

这里,

image.png

假设本金额为200美元,利率为每年6%,期限为8年,从上表中可以看出相应的FVIF是1.594(第8行最后一列)。该金额的未来价值如下:

image.png

在此程序中,LOC 1和2由include语句组成。LOC 3和4由define语句组成。LOC 5~29包含main()函数的定义。在LOC 7和8中,声明了一些变量。LOC 9~11由三个printf()语句组成,这些语句打印有关FVIF表的信息。LOC 12~17打印FVIF表的标题。LOC 18~25由嵌套的for循环组成,在这些嵌套循环中计算并打印FVIF表。LOC 26打印FVIF表的底线。







Leave a Reply

Your email address will not be published. Required fields are marked *