计算机专业编程语言类课程教学的思考
2016-05-20何炎祥湖北工业大学计算机学院湖北武汉430068武汉大学计算机学院湖北武汉430072软件工程国家重点实验室武汉大学湖北武汉430072
江 南,何炎祥(.湖北工业大学 计算机学院,湖北 武汉 430068;2. 武汉大学 计算机学院,湖北 武汉 430072;3. 软件工程国家重点实验室(武汉大学),湖北 武汉 430072)
计算机专业编程语言类课程教学的思考
江 南1,何炎祥2,3
(1.湖北工业大学 计算机学院,湖北 武汉 430068;
2. 武汉大学 计算机学院,湖北 武汉 430072;
3. 软件工程国家重点实验室(武汉大学),湖北 武汉 430072)
摘 要:从计算机专业编程语言类课程教学实践中总结3方面的教学思考,首先讨论如何在教学过程中提高学生准确分析程序运行结果的能力,即结合图示分析运行时数据区以及区分编译时和运行时错误,其次提出对比不同编程语言关键特性的教学手段,最后阐述培养抽象描述和表达问题能力的重要性,并以相应教学案例和取得的效果加以说明。
关键词:编程语言;运行时数据区;语言特性对比;抽象描述
1 提高学生准确分析程序运行结果的能力
程序完成一定的功能,功能的正确性是我们对程序的首要关注面,运行的结果就是设计和编码所要实现的预期效果。程序的运行结果是确定的,其中,“确定”这个词表达的意思是:运行结果一定是可以分析出来的,如果运行结果和预期不符,那么一定要找出问题所在。在教学过程中,常常听到学生说“这个程序的运行结果好象是……似乎是……”,有时候即使他们给出了一个正确的答案,也很难进一步说明答案的由来。因此,教师要在教学过程中对重要语法点设计相关联的程序,注重分析程序的运行结果并辅以图示,而不是一遍遍地罗列和解释每个语法点,让学生去记忆。一般来讲,计算机总是规规矩矩地按照程序编译后的指令去执行,因此,我们提倡的做法是按照语言的语法功能,用人脑代替机器运行程序,用笔和纸模拟机器执行的过程。这种方法在学生初学语言时对迅速掌握语法要点非常实用,并且在掌握一门语言后用这种方法分析复杂程序也是很有必要的。
1.1分析运行时数据区
讲授编程语言时,强调“一个程序不管有多复杂,它终归是该程序对所操纵数据产生的效果”。程序运行时环境,特别是数据区是非常值得关注的。作为教学案例,我们先给出一段Java参数传递的代码,其中程序的输出以注释给出。
假定类Person的定义包括一个整型(int)成员变量age、一个构造方法对成员变量进行初始化,两个成员方法getAge()和setAge(int)分别获得age的值和对age进行赋值。按照语法解释,该段代码的执行情况是:运行方法g()时,首先创建了一个Person对象,其成员变量age的值是10,本地变量p指向该对象,第二行输出10;使用p调用方法f时,按照Java按值传递参数机制,p的值传递给f方法的形参person,由于p是一个引用类型变量,它的值是对象的地址,这个地址值拷贝给person,因此person的值也是刚刚创建对象的地址,即person也指向这个Person对象;参数传递完成后,在f方法体中,首先创建了一个Person对象,其成员变量的值是20,person指向这个新创建的对象,因此输出20;方法f执行完毕后,返回到方法g,由于方法g在调用方法f时,没有对本地变量p产生任何改变,因此,输出10。
教师仅仅这样解释显得非常沉闷,如果给出它的运行时数据区图示,会直观和形象得多。图1中的(a)~(d)给出了调用方法g前后运行时数据区的变化。
图1 方法调用的运行时数据区示意图
在分析实现功能的层面上,模拟Java程序的运行需要关心的是栈桢中各本地变量的取值和堆中对象各成员变量的取值。因此,在分析上述代码时,按照程序的执行依次画出图1中的(a)~(d)。从图1可以直接看出,参数传递完成时,g栈桢中的person变量指向Person对象1;方法f执行完成后,person变量指向Person对象2;返回方法g后,f栈桢中的p变量仍然指向Person对象1。
上述运行时数据区的图示方法贯穿在整个授课过程对程序的分析之中。这种方法适用于常用的命令式语言,如解释C语言内存泄漏问题时,用图示法模拟运行时数据区的变化,可以直观描述被泄漏的内存空间。
实践表明,这种方法的使用形象化了知识点,加深了学生对语言理解,在授课过程中,我们鼓励学生使用这种方法来分析程序。从学生的反馈来看,这种方法对准确分析程序的运行结果有较大帮助。
1.2区分编译时错误和运行时错误
编译器不报错并不保证运行时没有错误发生。在教学过程中,为了培养学生准确分析程序运行结果的能力,我们还重视帮助学生建立编译时错误和运行时错误的概念,区分编译时错误和运行时错误。为了做到这一点,我们针对不同语言的特点,设计不同的代码段,解释它们的编译结果和运行结果。先以Java语言中的“造型”这一语法点为例。在Java中,造型分向上造型和向下造型,造型机制的语法点总结为:向上造型总是允许的,可以隐式进行;向下造型必须显式进行。我们可能写出看似符合这个总结的程序,但是,程序运行可能会报类造型异常的错误。假定类B是类A的子类,给出如下代码段:
在授课过程中,先给出代码段(1),分别创建了父类A的对象a和子类B的对象b,将子类型引用变量b强制转换为父类型(由造型操作(A)完成),即向上造型,然后赋给父类型引用变量a,这段代码既没有编译时错误也没有运行时错误;由于向上造型总是被允许的,即一个子类对象总是一个父类型,它可以隐式进行,去掉造型操作符(A)后,程序是不受影响的。然后给出代码段(2),它将一个父类型引用变量a赋给子类型引用变量b,即向下造型,因为无法知道一个父类型引用变量在运行时是否引用到一个子类型对象,向下造型必须显式进行,因此代码段(2)报编译时错误。接着给出代码段(3),显式造型父类型引用变量a为一个子类型(由造型操作(B)完成),这样,在编译时,编译器认为类型正确,不报错误;但是,在运行时进行类型检查时,由于引用变量a指向的对象是父类型的,将一个子类型变量引用到这个父类型对象时,Java运行时机制报类造型异常的错误。为了修正这个错误,最后给出代码段(4),即在向下造型前,将b赋给a,即将父类型的引用变量a指向子类对象,这样,程序运行时,引用变量a指向的对象是子类型的,将它赋给子类型变量没有任何问题。
对于C++编程语言,在VC++环境下,一个典型的编译时不报错,但运行时报错的例子如下:
int *p = (int *) malloc(2*sizeof(int));
free(p);
free(p);
它表示的是,指针变量p在运行时指向了一个整型数据空间,然后释放这个数据空间,最后它又再次释放这个已经释放的空间。许多大型C/ C++程序运行时错误是因为这种同一内存空间多次释放而造成的。需要说明的是,VC++和GCC环境对这类运行时问题的处理是不同的,给出这个例子是为了说明区分编译时错误和运行时错误对理解程序的必要性。
从“分析运行时数据区”和“区分编译时错误和运行时错误”两个方面阐述如何在教学过程中提高准确分析程序运行结果的能力。实践表明,这样能够加强学生对知识点的把握,使得这些知识点在学生的大脑有更清晰地认识,并加深学生对编程语言的理解。
2 对比不同编程语言的关键特性
不同编程语言在设计和实现上具有差异。C/ C++和Java是我们主要讲授的两种命令式编程语言,从程序形式上看,这两门语言较为接近,但它们在设计和实现上又具有明显的不同。在学习第二门编程语言时,如果适当结合已讲授的另外一门编程语言,会得到很好的触类旁通的效果,深化学生对编程语言的理解。
我们以面向对象语言的一个重要特性,多态为例。首先,设计具有对比性的两段代码,分别在VC++和javac下进行编译:
然后给出程序的分析:C/C++中,虚函数才会被覆盖,即子类函数覆盖了父类中声明为virtual的相同函数。当调用这样的函数时,运行时才会确定到底执行哪个类中的函数体,如果调用该函数的指针变量指向一个父类型,则执行父类中对应的函数;如果指向一个子类型,则执行子类中对应的函数。因此,当指针变量p指向a时,由于a的类型是父类型A,所以第1行和第2行分别输出A-f和A-g;当指针变量p指向b时,对于第3行输出,虽然b的类型是子类型B,但是函数f未声明为虚函数,f函数的调用在编译时已经由p的类型所确定,为父类型A,因此第3行输出A-f,对于最后一行输出,由于g函数声明为虚函数,而此时p指向的是一个子类型b,因此输出B-g。
Java代码中,Java语言没有virtual关键字,可以视为所有的方法都是虚方法,因此,B类中的方法f和方法g分别覆盖了A中的方法f和方法g。方法覆盖时,在运行时由调用方法的引用变量所引用对象的类型来确定到底执行哪个类中的方法体。对于第1行和第2行的输出,父类型的引用变量p指向的是一个父类对象a,则分别调用父类的方法f和方法g,因此输出A-f和A-g;在将父类型的引用变量指向一个子类对象b后,第3行和第4行的输出分别是调用类B中相应的方法体,因此输出B-f和B-g。
最后,引导学生对以上2个程序进行小修改,查看运行结果,思考为什么会是这样的运行结果,按照课堂上的分析,能否解释得通?按照学生下次课堂教学的反馈,引导有兴趣的学生阅读相关的编译器源码,对编程语言的实现进行粗略地介绍,减少编程语言在学生眼里的神秘感。
实践表明,语言的类似特性在不同语言中的对比教学是很有效果的。学生会给出一些类似“原来如此”的反馈,可见,这种教学手段能够澄清学生看来似是而非或者觉得百思不得其解的运行结果,也极大提高了学生对编程语言的学习兴趣,并能引导学生对后续课程,如编译原理等课程有基本认识。
3 培养抽象表达问题的能力
编程语言类课程的教学不仅要教会学生使用一到两门编程语言,更要在讲授知识的同时,让学生具有良好的抽象描述和表达问题的能力。
从功用来看,程序是用来解决问题的,为了解决问题首先需要描述和表达问题。它要求设计者站在计算机求解问题的角度,将问题抽象化。因此,在授课过程中教师如果过于关注语言本身的细节,可能学生学到的只是语言结构,这对解决问题是远远不够的,因为对程序设计没有宏观把握,不能写出好的软件。从这个意义上讲,编程语言类的教学应该在教学过程中,逐步引导学生建立一种实用的软件工程思想,这是非常重要的。
为了培养抽象表达问题的能力,教师在讲授程序的主要语法点之后,可以适当设计一些小案例作为问题提出,学生从已学的知识点中,将问题抽象化,然后开始编码实现、测试、调试并最终解决问题。其结果是既提高了学生对语法点的掌握程度,又培养了学生抽象描述和表达问题、解决问题的能力。
最后要强调的是,为了培养学生抽象表达问题的能力,掌握一门语言并具有扎实的编程技能是非常必要的。任何一门语言的熟练掌握和精通都需要花费努力和时间,不是短期可以实现的,因此在授课过程中我们一直强调并鼓励踏实的学习作风,尽可能激发学生的学习兴趣,使得他们在课程结束之后还愿意在编程语言应用能力的学习提高上投入精力。
4 结 语
具备一定的编程技能和具有抽象描述问题的能力是编程语言类课程需要达到的教学目标,本文基于计算机专业编程语言类课程的教学实践,对教学方法和思路提出思考。未来将继续结合教学实践,提高编程语言类课程的教学质量,并努力缩小与国外计算机编程类课程教学的差距。
参考文献:
[1] 曹西征, 孙志勇. VC++教学中实例驱动法和模糊评价研究[J]. 计算机教育, 2015(21): 82-84.
[2] Shewchun J . Data structures and Java programming [EB/OL]. [2015-11-30]. http://www.cs.berkeley.edu/~jrs/61b/.
[3] Sierra K , Bates B. Head fi rst Java[M]. 2 ed . Sebastopol: O'Reilly Media Inc, 2005.
[4] Sahami M. Programming methodology [EB/OL]. [2015-11-30]. http://web.stanford.edu/class/cs106a/.
(见习编辑:赵盼;编辑:宋文婷)
第一作者简介:江南,女,讲师,研究方向为编程语言、可信编译,nanjiang@whu.edu.cn。
基金项目:湖北工业大学教研项目“ RESTFUL Web服务环境下自主学习云系统的研究与实现”(2012015)。
中图分类号:G642
文章编号:1672-5913(2016)04-0156-04