涂小琴
(云南师范大学文理学院,昆明650222)
C语言结构化编程教学案例过程设计
涂小琴
(云南师范大学文理学院,昆明650222)
为了让学生更快速地掌握结构化编程方法,特设计此案例。利用自顶向下,逐步细化的方法将案例一步步推进,从问题的提出,分析问题,给出解决问题的基本框架,设计算法,任务分解,函数定义等几个方面进行详细的描述,最终解决问题。结构化程序设计思想是现代编程技术的基础,甚至是基础当中的基础,从提出问题、分析问题着手,给出解决问题的基本框架,再在框架上进行进一步的分析与细化,给出解决问题的全过程。将如何分析问题及解决问题表现得淋漓尽致。
C语言;结构化编程;教学过程;案例
人类进行程序设计只有几十年的历史,在最初进行软件生产的年代,软件的生产并没有什么章法可循,生产效率十分低下,失败的项目比比皆是。大约从有科学家反对goto语句开始,人类才开始认真思考软件应该如何编写,在这方面有限的若干成果之一就是结构化程序设计思想。
结构化程序设计思想是现代编程技术的基础,甚至是基础当中的基础,这个思想大体是20世纪的60年代末期和70年代,断断续续地由若干人提出并互相补充汇集而成的,目标是使程序的结构更清晰,更容易理解(可读性通常是衡量代码质量时仅次于正确性的一个指标,除了在对性能要求特别高的地方)、易于修改,易于调试,减少错误从而达到提高软件开发效率和成功率的目的。
自顶向下(top-down),逐步细化(stepwise refinement)是结构化程序设计思想的主要内容之一,而函数是实现这个思路的一种强有力的主要的技术支持手段。
要完成一个较大的任务,首先把它分解成若干个小问题;要完成一个复杂的问题,首先把它解析为若干个简单的问题。这就是自顶向下,逐步细化(或称之为“逐步求精”)的方法,先考虑整体,再考虑局部,最后考虑细节,下面以一个程序从构思到完成的完整过程来说明这种思路以及如何通过函数达到程序的实现。
题目分析:由于题目要求给出精确的结果,所以double,float这些数据类型显然不可能加以考虑,结果只可能以分数形式给出,然而C语言没有分数这种类型,那么就只能自已创造出这种类型——这就是所谓数据结构的含义。可以考虑以两个int类型的量来分别表示一个分数的分子和分母,这虽然是一种很初级、很粗糙的数据结构,但依据目前大一学生所学的内容,只能如此来表示。
程序的功能应该是输入一个整数n的值,然后输出H(n)的分子和分母,此外应该注意到输入非正整数是没有意义的,而且输入的n不可以太大。
前面描述了程序的输入也输出,实际上完成了对程序功能的定义,现在可以写代码了。/*程序功能:
输入:一个整数n的值。
输出:
n<=0时,输出“对于输入数值H(n)无定义”;
n太大的时候输出“数值太大无法计算”;
n>0且不是很大时输出H(n)的分子和分母。*/
结构化程序设计强调的是计划的正确性,然后逐步地渐进。可以用伪代码(Pseudocode),来描述程序员的思路并帮助程序员进行构思。可以在源代码编辑器中写成注释的形式,也可以作为程序的一个文档。伪代码或流程图,对于比较复杂的程序,能够明确程序所要完成的功能。
在确定程序的基本框架之前,首先需要确定程序涉及的数据对象的数据结构。由于程序的数据结构(用两个int类型分别表示和的分子与分母)已经确定,程序的基本功能已经明确,所以可以进一步勾勒出程序的基本结构和框架。
现在考虑n的值的界限问题。由于:
所以暂时把n!取值在int表示的范围内做评估n的上限的临界条件。由于12!=479001600,13!=6227020800,后者超出了int类型的表示范围,所以n的上限可以暂定为12。尽管离全部完成尚早,但是这段代码已经可以运行并接受程序测试了,编译完成后,运行并输入,-1,0,可以确信,程序的部分功能已经实现。/*程序功能:
输入:一个整数n的值。
输出:n<=0时,输出“对于输入数值 H(n)无定义”;n太大的时候输出“数值太大无法计算”;n>0且不是很大时输出H(n)的分子和分母。*/
这个程序只是完成了初步框架的搭建,程序并无语法错误,可以运行,结果如图1所示。
图1 运行结果图1
测试正确后,接下来专心考虑程序的核心算法。为了总结算法,一个切实可行的方法是自己用手工的方式把题目试着做几次(对于初学者尤其必要),如果使用纸张、笔都无法完成,就绝对不可能编程了。
假设n的值为6,计算的步骤如下:
可以看到当n为6时整个计算过程是分6次完成的,而每次进行的计算步骤是相同的。这显然可以用循环来描述。而且从上面可以看到,为了使计算的过程统一,和的分子的初值应该设为0,和的分母的初值应该设为1。在计算的过程中,每一项的符号都在发生变化,初始符号为正,接着为负,所以可以设,初始符号变量,fh=1,当进行下一项时,可以设定fh=-fh,这样可以对每一项做正负符号的改变。下面的代码再向前推进一步,完成变量hfz,hfm的定义,循环语句及输出的功能。
将第6行更新为:inthfz=0,hfm=1,fh=1;//int和的分子,和的分母;
将第19,20行更新为:
inti;
for(i=1;i<=n;i++)
//计算和的分子,和的分母;
{
//计算
}
//输出和的分子,和的分母;
printf("H(%d)为%d/%d ",n,hfz,hfm);
计算部分没法有完成,但是已经可以测试输出格式是否合乎要求了。可以执行printf(“H(%d)为%d/%d “,hfz,hfm);语句,但并没有完成计算,程序运行的结果也不正确,运行结果如图2。
图2 运行结果图2
这部分的计算是求两个分数的和(“1/i”与”和的分子/和的分母”)。计算的第一步是通分,而通分的本质是求两个分母(“i”与“和的分母”)的最小公倍数。在求得最小公倍数之后,可以利用最小公倍数求得通分后两个分数的新的分子(“最小公倍数/i”及“最小公倍数/和的分母*和的分子”)及它们的和,这个和就是“和的分子/和的分母”加上“1/i”后得到的新的“和的分子”。此时,可以把新的“和的分母”确定为刚刚求得的最小公倍数。最后对新得到的“和的分子/和的分母”还要进行约分(如果它们的最大公倍数不为1的话)。这样“//计算”部分的算法可以进一步细化为:
//(1)求“i”与“和的分母”的最小公倍数
//(2)用最小公倍数、“和的分子”、“i”、“和的分母”求新的“和的分子”;
//(3)新的“和的分母”=“最小公倍数”;
//(4)求“和的分子”、“和的分母”的最大公约数;
//(5)如果最大公约数不为1则进行约分。
以上的各个任务中,(3)可以简单的实现,(5)在目前还做不到用一个函数完成(因为要改变两个变量的值,而函数只能求得一个值),其余的都可以用一个函数来完成。
由于完成(1)需要两个 int类型的量(“i”与“和的分母”),求得一个int类型的量(最小公倍数),因而函数原型可写为:
int qiuzxgbs(int,int);
完成(2)需要5个int类型的量(“最小公倍数”、“和的分子”、“i”、“和的分母”、“符号位”),求得的新的“和的分子”是一个int类型的量,因而函数原型可以写为:
int qiuhe(int,int,int,int,int);
同理,(4)的函数原型如下:
int qiuzdgys(int,int);
函数定义及函数调用闻分在些不再详述,下面是完成后的代码。此外n的上限经过计算验证可以达到
23。
将第15行更新为:if(n>=24)
//求正整数m,n的最大公约数
intqiuzdgys(intm,intn)
{
inttemp;
while((temp=m%n)!=0)
{
m=n;n=temp;
}
return n;
}
//求正整数m,n的最小公倍数
intqiuzxgbs(intm,intn)
{
return m*n/qiuzdgys(m,n);
}
//求通分后分子之和
intqiuhe(intgbs,intfz,inti,intfm,intfh)
{
returngbs/fm*fz+fh*gbs/i;
}
重新将第19至20行更新为:
inti;
fh=-fh;
for(i=1;i<=n;i++)//计算和的分子,和的分母;
{
fh=-fh;
intzxgbs,zdgys;//存放最小公倍数,最大公约数
//求"i"与"和的分母"的最小公倍数(1)
zxgbs=qiuzxgbs(hfm,i);
//用最小公倍数、"和的分子"、"i"、"和的分母"求新的"和的分子"(2);
hfz=qiuhe(zxgbs,hfz,i,hfm,fh);
//新的"和的分母"="最小公倍数";(3)
hfm=zxgbs;
//求"和的分子"、"和的分母"的最大公约数(4);zdgys=qiuzdgys(hfz,hfm);
//如果最大公约数不为1则进行约分(5)。
if(zdgys>1)
{
hfz/=zdgys;
hfm/=zdgys;
}
}
之后的任务就是程序的测试与调试了,程序运行正确,运行结果如图3所示。
图3 结果运行图3
从以上的过程可以知道,要解决好一个问题,需明确以下几点:
(1)明确定义程序的总体目标或功能,而且越明确越好。可以把这种目标作为注释写在代码中或其他文档中,这是编程总的依据。
(2)将程序的目标粗略地分解为若干比较简单的问题,这时可以在保证程序结构正确性的前提下勾勒出main()在大致框架。
(3)审视main()的结构和框架,确保无误之后可以开始逐个考虑(1)中有没有具体完成的各个函数定义。这些函数本身可能依然是比较大或比较复杂的任务,同样按照(1)的方式明确这些函数的功能,然后继续把这些函数解析成更小或更简单的任务。
(4)在进行(1)、(2)时尽量作到每个步骤都保证代码在语法上的正确性,可以用空函数、空语句、假设的数据以及注释来达到这个目的。这样便于在程序编写过程中进行不断测试以保证程序在总的大框架范围内的正确性。同时使得编程以“一步一个脚印”的方式进行,有利于编程者对程序的正确性和进度增强信心。
(5)对各个功能模块进行透彻的分析并且明确地写出来,这样才能在进行完每个步骤之后都能进行程序测试,而这种测试对于编程有极大的好处。从前面的例子可以看到,结构化程序设计有些像绘画时先打个草图或先画个轮廓,然后再逐步把各个部分绘制好一样。实际上生活中到处都可以看到top-down这种思想的影子,结构化程序设计不仅使得程序本身变得有条理、层次清晰,每个部分的结构和意图都清晰可见,实际上也使得编程过程本身变得很有条理,具有可规划性。在这种思想的指导下,编程的步骤本身也变得井井有条。
[1]王敬华.C语言程序设计.第二版[M],2009.
[2]黑马.C语言程序设计案例式教程[M],2017.
涂小琴(1981-),女,讲师,研究方向为程序设计方法
Teaching Case Process Design of C Language Structured Programming
TU Xiao-qin
(College ofArts And Sciences,Yunnan NormalUniversity,Kunming 650222)
Design this case,in order to let students more quickly master structured programming method,the top-down method is used to push the case forward step by step,from the pointofview ofthe problem,describes the analysis ofthe problem,the basic framework for solving the problem,the design algorithm,the task decomposition,function definition and other aspects in detail,finally solves the problem.The idea ofstructured programming is the basis ofmodern programming techniques.Starts with asking questions and analyzing problems,and gives the basic frame ofsolving the problems,further carries outthe analysis and refinementon the framework,gives the whole process ofsolving the problem.
C Language;Structured Programming;Teaching Process;Case Study
1007-1423(2017)30-0072-05
10.3969/j.issn.1007-1423.2017.30.016
2017-09-05
2017-10-20