浅谈 《C语言程序设计》课程教学中学生编程能力的培养问题*
2013-09-13赵仁庆邓燕林
刘 鹏,赵仁庆,邓燕林
(楚雄师范学院数学系,云南 楚雄 675000)
C语言是一种国际上广泛流行的计算机高级语言,具有用途广泛、功能强大、使用灵活的特点。《C语言程序设计》是高等学校的一门基本的计算机课程,在计算机教育和计算机应用中发挥着重要的作用。通过学习程序设计,学生进一步了解计算机的工作原理,更好地理解和应用计算机;掌握用计算机处理问题的方法,能培养分析问题和解决问题的能力,具备编制程序的初步能力[1]。
C语言是大学阶段第一门程序设计的课程,有了C语言的基础,在以后学习高级语言编程就会很容易。在该课程的教学中,如何来培养学生的编程能力呢?
1.牢记程序设计的思想
结构化程序设计的思想是自顶向下,逐步细化,模块化设计,结构化编码。核心的理念就是层次。良好的代码要有层次感,先做好“顶层设计”,然后一步一步细化,这样做可使编码工作有条不紊地进行。
在教材第291页有这样的题目[1]:有n个人围成一圈,顺序排号。从第1个人开始报数 (从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。文献[2]在第104页给出如下的参考解答[2]:
这段代码从头到尾只有一个main()函数,根本就不符合结构化程序设计的思想要求。
把所有的代码都堆在main()函数中,就如同在一个硬盘中存放了很多文件,却没有使用任何文件夹来进行管理一样;也如同买了一套房子,并没有用墙把房子分隔成卧室、客厅、厨房、餐厅、卫生间,而是将一切生活用品都堆放在房子的地上,吃喝拉撒都在一个大房间里进行。
一个源程序由若干个函数组成,犹如用墙把房子分隔成一个个房间一样。模块化设计就是把一个大任务分为若干个相对简单的子任务,每个子任务用函数来实现。
解决一个问题首先要从大处着手,做好“顶层设计”。站在高处来看,解决这个问题需要四个步骤[3]:输入人数;数组初始化;“从1到3报数”n-1次;输出最后剩下的那个人的编号。而每一步都可以简单地用函数或基本的控制语句来实现。在向下一层展开之前应检查本层设计是否正确,只有上一层设计是正确的才能向下细化。良好的程序设计应该是把复杂的问题分解为简单的问题,然后各个击破,一件一件有条不紊地完成。
“自顶向下,逐步求精”的层次理念是每个编程人员时刻都要坚持的,就像做人要守好自己的“底线”一样,不能轻易发生动摇。
2.归纳总结编程的标准模板
在教学的过程中,可以总结出一个编程的标准模板。利用这个模板,可帮助学生快速搭建程序框架,让学生能集中精力于数据的处理、问题的解决方面。这个模板为:
下面就以求两个正整数的最大值为例进行说明:
第一:预处理命令#include<stdio.h>必不可少,另外可能用到的有#include<math.h>、#include<string.h>。若程序中有一些常数,建议用预处理命令将其定义为符号常量:#define N 10,另外要注意预处理命令后面没有标点符号。
第二:函数声明由函数定义中的首行,再加一个分号“;”形成,此时形参名可省略不写。形参名是什么都无所谓,特别注意函数声明是以分号“;”结束的。
第三:根据问题的需要,定义有关的变量,在使用的过程中一定要注意与定义时的变量类型要保持一致。全局变量一般是在不同的地方进行传递数值的。
第四:数据的输入主要有以下几种方式:①赋值:在定义时进行赋值即为初始化,如数组的初始化;②键盘输入:特别注意做好数据输入 (出)时的提示设计,如:
③加入数据的合法性检查:数据的合法性检查是数据输入时要做的事,当用户输入不合法的数据,程序提示用户重新输入,如:
第五:数据处理是程序设计编写的核心,一般使用函数来完成,这样一来使设计者的精力放在后面的函数定义上即可:c=max(a,b);。
第六:数据的输出主要用函数printf()实现:
字符数据的输入输出函数有:putchar,getchar,puts,gets。
第七:函数定义就是程序设计者要花时间精力去做的事情,如:
3.掌握一些基本的算法和常见问题的求解方法
有了模板后,要写好程序就需要写好函数,而要写好函数就必须熟悉一些基本的算法和常见问题的求解方法。
第一个问题:用辗转相除法,求解整数x和y的最大公约数的算法如下[4]:
步骤1:变量r是x除以y的余数;
步骤2:变量r不是0的时候,反复执行步骤3~5;
步骤3:变量x代入变量y的值;
步骤4:变量y代入变量r的值;
步骤5:变量r是x除以y的余数;
步骤6:变量y的值即为最大公约数;
使用基本语句:
一般来说,只要能得到问题的解法,就很容易把它写成函数的形式。该问题从使用穷举法、基本语句到使用函数、递归函数的过程,体现了编程中精益求精的思想,极能体现编程思维的抽象性和深刻性。
第二个问题:判断键盘输入的整数 (用n来表示)是否是素数:
根据数学上的结论,循环只需到n的算术平方根就可以了。但此时不能简单地用sqrt(n)来求n的平方根或整数部分,由1=12,1+3=22,1+3+5=32,…,1+3+…+(2k-1)=k2,可求出n的整数部分。整个程序如下[3]:
素数问题本身就是初等数论中的重要问题,解决该问题的程序效率与所掌握的有关数学理论知识密切相关,这里给出的求n的整数部分的方法值得程序设计者深思。
第三个问题:级数求和中典型的例子是[1]:用π/4≈1-1/3+1/5-1/7+…公式求π的近似值,直到某一项的绝对值小于106为止 (该项不累加)[1]。
该问题之所以重要是因为涉及到循环累加求和基本方法、交错数列符号处理技巧,真正掌握该编程思想就能处理许多求和的问题。
这类问题还很多,需要在学习过程中不断地归纳总结。通过对这些问题的解决,可训练学生的编程思维,进一步提高学生的编程能力。
4.树立正确的观念
现在介绍IT行业优秀工程师的经验方面的书籍很多,里面有许多值得在教学中推广的做法。当然,在以往教学中也存在许多错误的观念,会严重影响教学的质量。
一些正确的观念,在教学之初就要强调:①浮点数是实数在计算机上的近似表示,只能表示一部分实数;②程序设计中,遇到常数一般定义为符号常数,避免魔法数问题 (程序中的常数与问题没有直接关系);③写循环时一定要检查边界条件,避免造成失之毫厘,差之千里的错误;④指针是某个对象存储时占用若干连续存储单元中首个存储单元的地址。这方面的问题建议大家好好读读参考文献[3]、[4]~[8]。
许多初学者都会有这样的想法:只要编写出程序,运行结果也没有问题就万事大吉了。实际上这是一个很严重的错误观念!比如求100~200间的全部素数的程序如下[1]:
该程序编译、连接都没问题,执行结果如下
从数学的角度上来说结果也没有错误。但该程序隐藏着一个很难发现的错误[3]:语句if(m%10==0)printf(" ");的正确位置是在m=m+1;之后 (应与前面的花括号互换位置)!程序中if(m%10==0)printf(" ");语句的意思是前面输出了10,20,30,……个素数时就换行,即只要前面输出的素数个数是10的倍数就换行。该问题中第10个素数与第11个素数以及第20个素数与第21个素数恰好相邻,掩盖了这个错误。将语句中的10改为其他正整数如3、4等,错误马上就凸显出来。
实际上这里使用该语句是想每行输出10个素数,只有语句if(m%10==0)printf(" ");紧随语句m=m+1;之后,才能真正实现这一目的。
5.注重实践操作
教师平时都会告诫学生:看懂学会书上的理论知识,学习只成功了30%;动手编程,亲自上机调试运行,可达60%;善于利用所得到的知识解决实际问题,才能达到90%以上。学习C语言程序设计,必须重视实践环节,既会编写程序,又会上机实践,另外还要举一反三。
6.注意在后续学习中培养学生的编程能力
《C语言程序设计》课程只是程序设计学习过程中的起点,在今后的学习过程中,还需要进一步学习其他高级程序语言,遇到许多实际问题时要有编程解决的意识。只有养成用程序设计来解决实际问题的良好习惯,才能使程序设计的学习得到深化,程序设计能力才可能会得到提高。
如学生在后续课程《信息论与编码》中需要计算信息熵:
其中p1,p2,…,pn为概率分布:p1+p2+…+pn=1,pi〉0。
C语言数学库中没有log2(x)函数,可用换底公式来计算:log2(x)=log(x)/log(2)。函数的参数个数是变化的,C语言中没有专门的函数,参考程序为:
《C语言程序设计》课程教学中学生编程能力的培养问题是一个系统工程,只有坚持不懈地努力,才能收到良好的效果。
[1]谭浩强.C程序设计 (第四版)[M].北京:清华大学出版社,2010:9,131-132,137-138,291.
[2]谭浩强.C程序设计 (第四版)学习辅导[M].北京:清华大学出版社,2010:104.
[3]薛非.抛弃C程序设计中的谬误与恶习[M].北京:清华大学出版社,2012:86-87,255.
[4]李克秋译.算法解读[M].北京:科学出版社,2012:100.
[5]尹哲,郑秀雯译.编写可读代码的艺术[M].北京:机械工业出版社,2012.
[6]陈正冲.C语言深度解剖——解开程序员面试笔试的秘密[M].北京:航空航天大学出版社,2010.
[7]崔康译.程序员的思维修炼开发认知潜能的九堂课[M].北京:人民邮电出版社,2011.
[8]何海涛.剑指Offer名企面试官精讲典型编程题[M].北京:电子工业出版社,2012.