程序员在利用软件开发方法解决实际问题时,很少将每一个新程序看作一个孤立事件。包含在问题描述中的信息以及在分析和设计阶段收集的信息能帮助程序员设计并实现完整的程序。程序员也可利用以前程序解决方案中的一部分作为构件来构造新程序。
本章的第一部分示范了如何以预定义函数的形式用已有信息和代码来写程序。除了利用已有信息,程序员还可以使用自顶向下方法来简化算法开发和程序的结构。应用自顶向下设计时,程序员最初用最宽泛的描述解决问题,然后进入更细节的子问题。本章的第二部分演示了自顶向下设计,并强调了利用函数实现模块化设计的作用。
3.1 利用已有信息编程
程序员在开发程序的时候很少凭空开始,而通常会从已有信息或者从另一个问题的解决方案中得出部分或全部的解决方案,本节将进行示范。
若遵循软件开发方法,我们甚至在开始编程之间就要编写系统文档。系统文档包括问题的数据需求(分析阶段)和解决算法(设计阶段),并总结编程目的和思考过程。
系统文档可以作为编程的起点,也就是遵守C语言中常量宏定义和变量声明的语法编写数据需求,例如图3-1中英里到公里的转换程序。而且如果系统文档由文字处理器产生并且可编辑时,这种方法非常有效。
|
1. /* 2. * Converts distance in miles to kilometers. 3. */ 4. 5. #include <stdio.h> /* printf, scanf definitions */ 6. #define KMS_PER_MILE 1.609 /* conversion constant */ 7. 8. int 9. main(void) 10. { 11. double miles; /* input - distance in miles. */ 12. double kms; /* output - distance in kilometers */ 13. 14. /* Get the distance in miles. */ 15. 16. /* Convert the distance to kilometers. */ 17. /* Distance in kilometers is 18. 1.609 * distance in miles. */ 19. 20. /* Display the distance in kilometers. */ 21. 22. return (0); 23. } |
图3-1 转换程序的数据需求和算法
为了在主函数中开发可执行代码,首先利用初始算法及其细化算法作为程序注释。注释可以描述算法步骤,还可以作为程序文档指导C代码编程。图3-1表示了这时程序的概貌。在主函数中写好注释后,就可以在每一算法步骤下直接开始写C语句了。在某一细化步骤下,可以将细化语句从英语转换到C语言,或者直接用C代码来代替。在下一个实例研究中将介绍整个过程。
实例研究:求圆的面积和周长
问题
先得到圆的半径,然后计算并显示圆的面积和周长。
分析
很明显,问题的输入应该是圆的半径,输出应该是圆的面积和周长。因为输入和输出可能包含小数部分,因此这些变量都应该是double型。圆的半径与面积、周长的关系,以及数据需求在下面列出。
数据需求
问题常量
PI 3.14159
问题输入
radius /* radius of a circle */
问题输出
area /* area of a circle */
circum /* circumference of a circle */
相关公式
圆的面积 = π ×半径2
圆的周长 =2π×半径
设计
在确定了问题的输入和输出之后,列出解决问题需要的步骤,要特别注意各步骤的顺序。
初始算法
(1) 得到圆的半径。
(2) 计算面积。
(3) 计算周长。
(4) 显示面积和周长。
算法细化
下面细化还未明确的步骤(步骤2和步骤3)。
步骤2细化
2.1 将PI * radius * radius赋给area。
步骤3细化
3.1 将2 * PI * radius赋给circum。
实现
图3-2给出了到目前为止的C程序。主函数将初始算法及其细化算法作为注释列出。要得到最终的程序,还需要将细化部分(步骤2.1和步骤3.1)和未细化部分(步骤1和步骤4)写成C代码。最终的程序在图3-3中给出。
测试
由于根据半径值5.0很容易算出圆面积和周长,因此图3-3的输出结果可以证明程序是否正确。半径的平方是25.0且π接近于3,因此面积是正确的。周长应该是10π,也很容易由手算得出结果。
|
1. /* 2. /* Calculates and displays the area and circumference of a circle 3. /*/ 4. 5. #include <stdio.h> 6. #define PI 3.14159 7. 8. int 9. main(void) 10. { 11. double radius; /* input - radius of a circle */ 12. double area; /* output - area of a circle */ 13. double circum; /* output - circumference */ 14. 15. /* Get the circle radius */ 16. 17. /* Calculate the area */ 18. /* Assign PI * radius * radius to area. */ 19. 20. /* Calculate the circumference */ 21. /* Assign 2 * PI * radius to circum. */ 22. 23. /* Display the area and circumference */ 24. 25. return (0); 26. } |
图3-2 圆程序概貌
|
27. /* 28. * Calculates and displays the area and circumference of a circle 29. */ 30. 31. #include <stdio.h> 32. #define PI 3.14159 33. 34. int 35. main(void) 36. { 37. double radius; /* input - radius of a circle */ 38. double area; /* output - area of a circle */ 39. double circum; /* output - circumference */ 40. 41. /* Get the circle radius */ 42. printf("Enter radius> "); 43. scanf("%lf", &radius); 44. 45. /* Calculate the area */ 46. area = PI * radius * radius; 47. 48. /* Calculate the circumference */ 49. circum = 2 * PI * radius; 50. 51. /* Display the area and circumference */ 52. printf("The area is %.4f\n", area); 53. printf("The circumference is %.4f\n", circum); 54. 55. return (0); 56. } Enter radius> 5.0 The area is 78.5397 The circumference is 31.4159 |
图3-3 计算圆的面积和周长
实例研究:计算一批平垫圈的重量
程序员利用已有信息的另一种方法是通过一个问题的解决方案来解决另一个问题。例如,本题的解决方案可以建立在前一个例题的基础上。
问题
在生产平垫圈的硬件公司内,需要一个程序来计算一定数量平垫圈的重量,以便预估运输费用。
分析
平垫圈类似于一个油炸圈饼。要计算出单个平垫圈的重量,需要知道它的环形面积、厚度以及材料的密度。后两者是问题的输入,而环形面积(见图3-4)需要由两个输入量来计算,它们是垫圈的外径和内径(圆孔的直径)。

图3-4 计算平垫圈的环形面积
下面的数据需求中将垫圈的内半径、外半径、环形面积和单个垫圈(unit_weight)的重量作为程序变量。
数据需求
问题常量
PI 3.14159
问题输入
double hole_diameter /* diameter of hole */
double edge_diameter /* diameter of outer edge */
double thickness /* thickness of washer */
double density /* density of material used */
double quantity /* number of washers made */
问题输出
double weight /* weight of batch of washers */
程序变量
double hole_radius /* radius of hole */
double edge_radius /* radius of outer edge */
double rim_area /* area of rim */
double unit_weight /* weight of 1 washer */
相关公式
圆的面积 = π×半径2
圆的半径 =直径/2
环形面积 = 外环面积-圆孔面积
单个重量 = 环形面积 × 厚度 × 密度
设计
下面列出算法和步骤3和步骤4的细化。
初始算法
(1) 得到垫圈的内径、外径和厚度。
(2) 得到材料密度和生产的垫圈数量。
(3) 计算环形面积。
(4) 计算单个平垫圈的重量。
(5) 计算这批垫圈的重量。
(6) 显示这批垫圈的重量。
步骤3细化
3.1 计算hole_radius和edge_radius。
3.2 rim_area = PI* edge_radius * edge_radius – PI * hole_radius * hole_radius
步骤4细化
4.1 unit_weight = rim_area * thickness * density。
实现
写程序时,将数据需求作为变量声明,并将初始算法和细化算法作为编写可执行代码的出发点。图3-5给出了C程序。
|
57. /* 58. * Computes the weight of a batch of flat washers. 59. */ 60. 61. #include <stdio.h> 62. #define PI 3.14159 63. 64. int 65. main(void) 66. { 67. double hole_diameter; /* input - diameter of hole */ 68. double edge_diameter; /* input - diameter of outer edge */ 69. double thickness; /* input - thickness of washer */ 70. double density; /* input - density of material used */ 71. double quantity; /* input - number of washers made */ 72. double weight; /* output - weight of washer batch */ 73. double hole_radius; /* radius of hole */ 74. double edge_radius; /* radius of outer edge */ 75. double rim_area; /* area of rim */ 76. double unit_weight; /* weight of 1 washer */ 77. 78. /* Get the inner diameter, outer diameter, and thickness. */ 79. printf("Inner diameter in centimeters> "); 80. scanf("%lf", &hole_diameter); 81. printf("Outer diameter in centimeters> "); 82. scanf("%lf", &edge_diameter); 83. printf("Thickness in centimeters> "); 84. scanf("%lf", &thickness); 85. 86. /* Get the material density and quantity manufactured. */ 87. printf("Material density in grams per cubic centimeter> "); 88. scanf("%lf", &density); 89. printf("Quantity in batch> "); 90. scanf("%lf", &quantity); 91. 92. /* Compute the rim area. */ |
图3-5 平垫圈程序
|
93. hole_radius = hole_diameter / 2.0; 94. edge_radius = edge_diameter / 2.0; 95. rim_area = PI * edge_radius * edge_radius - 96. PI * hole_radius * hole_radius; 97. 98. /* Compute the weight of a flat washer. */ 99. unit_weight = rim_area * thickness * density; 100. /* Compute the weight of the batch of washers. */ 101. weight = unit_weight * quantity; 102. 103. /* Display the weight of the batch of washers. */ 104. printf("\nThe expected weight of the batch is %.2f", weight); 105. printf(" grams.\n"); 106. 107. return (0); 108. } Inner diameter in centimeters> 1.2 Outer diameter in centimeters> 2.4 Thickness in centimeters> 0.1 Material density in grams per cubic centimeter> 7.87 Quantity in batch> 1000
The expected weight of the batch is 2670.23 grams. |
图3-5(续)
测试
为了测试程序,可分别以2厘米和4厘米的内径和外径运行程序,这样可以轻松得出环形面积(3 * PI平方厘米)。另外,可以证明在输入数量为1时,程序计算出了正确的单个垫圈重量,然后用更大的数可证明程序对一批垫圈也是正确的。
练习
自测
1. 在给定员工工作小时数和时薪的情况下,计算薪资总额。要求写出问题的输入和输出,并写出程序的算法。
2. 写出自测题1中解决方案的初步程序。要求写出程序的声明部分,以及算法和细化部分对应的程序注释。
3. 在自测题1中计算薪资总额时,如果要求在算法中包含加班时薪为平时时薪的1.5倍,程序需要作什么改变?这里假设加班时间是单独输入的。
编程
1. 在下面的程序框架中加入细化部分,并写出最终的C程序。
/*
/* Compute the sum and average of two numbers.
/*/
#include <stdio.h>
int
main(void)
{
double one, two, /* input-numbers to process */
sum, /* output-sum of one and two */
average; /* output-average of one and two */
/* Get two numbers. */
/* Compute sum of numbers. */
/* Compute average of numbers. */
/* Display sum and average. */
return (0);
}
2. 写出自测题1的完整C程序。
3. 写出在自测题3中修改后的计算薪资总额算法的完整C程序。
4. 假设平垫圈是由一种均匀厚度的矩形材料冲压而生成的,扩展垫圈程序,计算(a)生产指定数量的平垫圈所需材料的面积,(b)剩余材料的重量。







