C语言教学探讨
2016-11-14郭亮郭海智
郭亮 郭海智
摘要:针对C语言教学过程中存在的问题并结合自身教学实践,提出几点具体建议。包括从类型和变量出发,自教学初期就加强对语言中的一些核心问题讲解,并在课程教学前期的语言基础部分对此进行反复强调及紧密联系,后期再专注于程序设计能力及计算思维的培养。
关键词:C语言;类型;指针;函数
中图分类号:G642 文献标识码:A 文章编号:1009-3044(2016)24-0116-02
Abstract: Aiming at the existing problems in the process of C language teaching, combined with personal teaching practice, giving some suggestions. Starting from the types and variables, the suggestions include strengthen the interpretation of some core elements in C language at the primary stage of teaching, emphasize the interrelation of these elements, later stage focus on training the ability of programming and computational thinking.
Key words: C language; type; porinter; function
C语言高效、灵活且兼具高级和低级语言的特点,使其应用及影响极其广泛,并早已成为各大高校的第一门程序设计课程。该课程信息量大且地位显著但授课时间有限,作为重点和难点的指针等概念也提高了学习门槛,导致教学效果普遍不理想,影响了后期多门专业课程的开展。
1 教学中的问题
C语言教学过程中除了需要平衡语言特性和程序设计能力培养外,还应针对教学对象的特点进行内容的合理编排,加强各个知识点的联系,降低学习曲线的陡峭程度。当前众多教材及教学过程在内容编排上采用类似自顶向下的教学方法,简述变量、常量等基础概念后,在控制结构中开始程序设计能力的初步培养,到数组、指针及函数参数部分转而强化底层概念,最后通过结构体、综合示例及简单数据结构等回归到设计[1]。这样的教学过程可能会使部分学员在对前期语言基本要素掌握不到位的基础上产生一些错误理解,后期再来修正就比较困难。
结合个人教学经验及我校的教学情况,在课程编排上应尽早让学员理解并熟悉底层概念,尽早引入函数概念并持续培养模块化程序设计方法[2]。前期教学重点讲述核心概念及常规用法,加深学员对语言整体的理解,后期重点培养程序设计和计算思维能力①。
2 具体改进
2.1 坚持清晰第一
任何课程,每次课堂的教学都应遵从清晰第一原则,C语言教学也不例外。清晰第一的一个功能是重点突出。对C教学而言,讲解所用的代码表述要保证清晰明了,不要过多讨论i+++j等问题,避免由于写法的技巧性和语法的复杂性加重学员学习负担。有文章列出的一些“有趣C问题”并不能提高大部分初级学员的兴趣,反而可能会使其产生挫败感并导致厌学心态,这样的东西在教学初期应该适度引入。清晰第一的另一个功能是避免了部分的不可移植性问题及潜在的错误。在C语言中存在表达式计算的副作用和顺序点问题,对int i=1;f(i++)+g(i--)没有约定是进行f(1)+g(1)还是f(1)+g(2),利用本原则可以消除由于此类语义表述所埋下的隐患,突出避免编写依赖特定计算顺序表达式的重要性。另外,清晰第一还体现在可读性上,排版、注释、标识符命名及编程风格等方面,需持续培养学员良好的编程习惯,尽力使教学与实际开发保持一致。清晰第一要避免对晦涩用法的讲解和考核但不是简单反对和抛弃惯用的简洁描述,课堂上应该给出习惯用法以体现C语言的灵活性。
2.2 强化类型概念
类型体现了抽象,抽象是人认识客观世界的主要能力。类型作为计算机科学的核心概念在众多C语言教材中阐述并不多,教学过程中理应让学员对此留下深刻印象。讲解中通过整数的补码表示,字符型的ASCII编码等让学员明确计算机对不同信息的分类表示。由此引出,类型约定了表示的形式,进而限制了存储空间的大小和取值范围。初期讲解常量时用printf( )的不同格式描述符验证int和double的编码不同,结合sizeof运算符验证不同类型的存储大小,并在介绍位运算后深入了解整型的补码编码方式。利用求余运算符的两个操作数为整数而非浮点数,验证类型对操作的限定,引出类型在另一方面又约束了其上的相关操作。类型概念应该在课程教学的各个章节进行强化,比如后续讲解指针运算只有加减和比较操作时可再次强调。同类型之间可直接操作,异类间操作需先统一为同种类型,重视了类型问题也就便于掌握类型转换,减少了转换出错的概率。面向对象的程序语言强化了C语言本就具有的这个核心概念,并发展到了一个新高度,C中类型的这两个能力与面向对象的理解基本一致,掌握好该核心概念也有利于后续面向对象程序设计语言的学习。
2.3 深入理解变量与赋值适度引入指针
C语言兼具高级语言和低级语言的特点,其实现与内存关系紧密。变量的功用是保存信息以便后续使用,信息的类型不同变量类型的选择也就不同。讲解时无需简单罗列所有变量类型,建议把整型变量单拿出来细讲,这样学员理解好了一个,也就方便了后续其他类型的学习。让学员理解变量名屏蔽了底层地址信息所带来的使用方便性,以及掌握利用底层地址信息进行间接访问操作的一致性。对整型变量int i;通过printf(“%d,%p”,i,&i)引出%p格式描述符和&取地址运算符以验证变量的存储内容和存储起始地址,并指出变量初始化的重要性。进一步用printf(“%d,%p,%d”,i,&i,*&i)引出用地址求值的间接访问运算符*,为指针的引入建立基础。C语言的赋值并非数学中的相等比较,示例中用i=1与j=i,强调i在等号的左边表示存储位置,在等号的右边表示存储内容,由此引出左值和右值概念,明确左值是能指代存储空间的对象,从而避免i++++此类错误操作。通过赋值操作符设问如何保存地址信息从而完整地引出指针类型,用示例强化指针相关的两个重点操作&,*。再次结合sizeof查看多个同类型变量的内存分布,以引出指针的加减及比较操作,并由不同指针类型间转换引出void *这个类型转换桥梁,以方便后期数组、结构体等教学。至此,完成对指针涉及的所有基础知识讲解,后续课程重点讲其应用即可。有了这些概念后引出scanf以尽快开启交互式程序的设计,提高学习兴趣。
2.4 尽早引入函数
个人认为很多教材将函数放在课程后半部分讲是不合理的。为了便于学员理解到函数及模块化的重要性,尽早接触到结构化程序设计思想,建议在赋值语句和表达式之后就以“命名的语句块”方式初步引出函数概论以熟悉基本框架和使用,并在后续章节逐步深入。在控制结构中结合函数进行基本编程能力的培养,重视函数设计的通用性[3]。讲解函数时,不必按参数、返回值等对函数进行分类,重点应该从主调和被调,从使用和实现的角度出发,对比两者对函数的关注点和关注内容的不同,体现出接口的重要性,并让学员在设计与调用时做到清晰明了。需强调函数参数的值传递方式而无需引入地址传递等其他表述以减轻学习负担。对函数调用栈状态变化的讲解要简明扼要,以清晰值传递的实现基础,并体会语句块内局部变量的概念,为后续变量作用域和存在期讲解打下铺垫。在具有指针的基础概念上对比void func1(int a){a++;}和void func2(int *a){(*a)++;},前者用复印件的类比说明修改不涉及原件,后者用钥匙的类比说明直接修改原件,以及利用2值交换函数理解函数内修改函数外数据时利用指针间接访问所带来的便利性。由于程序运行时存储在内存,其中的所有对象都有地址信息也包括函数,由此引出函数指针及其基本用法。关于递归的讲解涉及的是程序设计方法与机器细节相关不大,重点通过简单示例强调思维方式。对汉诺塔、二叉树遍历等经典例子以及数据结构与算法基础内容等可在课程的后半部分单设常用编程技术章节专门提高程序设计能力。
2.5 清晰数组概念
由于前述教学安排,数组可放到指针及函数之后讲解。对该概念的引入可以提两点,一是便于大量同类型变量的定义及使用,int a[1000]比a1,a2...a1000方便定义,下标操作方便使用;二是内存采用连续地址空间实现数组,存储的空间顺序性保证了元素间关系的顺序性,使得数组成为多种数据结构实现的基础。强调数组名代表整个数组对象从而不具有左值特性,也就无法进行整体赋值和a++等操作。对数组名用sizeof(a)计算整体占用空间,引出sizeof(a)/sizeof(a[0])的习惯用法。强调数组名a的类型类似int *const指针,值为头元素地址,即a等值于&a[0],可对其加减以获得特定指向。结合int (*p)[1000]=&a;指出a,&a二者值相等但类型不,并在示例中利用a+1,&a[0]+1,&a+1结合内存模型进行验证。既然数组名类似指针,也就引出数组表示和指针表示的统一性,即a[2]等价于*(a+2),由加法交换律*(a+2)等价于*(2+a),也就解释了2[a]这种奇怪写法的存在,同理也解释了1[“abc”]是什么。对数组用a[-1],a[-2]示例编译器不进行访问越界的检测,体会不可控越界的危险性。联系到函数参数传递时,用swap(int a[],int b[])与swap(int *a,int *b)完成2值交换的一致性验证数组名退化为指针类型,引出参数传递中一维数组长度信息的丢失问题,总结出函数参数中应该带入数组长度信息func(int a[],int length),并结合二维及多维数组再次强调参数传递忽略最高维的原因。后期涉及结构体讲解时,以struct B{ int i[3];}b1,b2;b2=b1;做对比,突显出结构体整体赋值的特殊性,并以此讨论数组与结构体在进行函数参数传递的成本问题,以体现指针的优势。
3 结语
关于程序设计能力和计算思维的培养是个长期过程,需要在整个培养体系中利用多门课程和实践进行贯穿和深入。实践证明,当前这种教学编排强化了学员对上述重要概念的理解,知识点间联系更紧密,对事物的前因后果更清晰,在一定程度上减少了学习弯路,提高了教学效果。
参考文献:
[1] 谭浩强.C程序设计[M].4版.北京:清华大学出版社,2012.
[2] 裘宗燕.从问题到程序——程序设计与C语言引论[M].2版.北京:机械工业出版社,2011.
[3] 朱安平.一种C语言函数教学的设计与实践[J].计算机工程与科学,2014,36(S1):139-142.