C语言程序设计课程中指针的几个教学要点

田维莲 羊巍 六盘水师范学院

C语言程序设计教学中,指针的教学是重难点;
此时,学生已经学了函数、数组及结构体等内容,而在指针与函数、指针与结构体及文件的教学中,都需要学生对指针有着深刻的理解与掌握,同时,在其后续课程数据结构中,对指针有很高要求,学生如果不掌握指针并灵活运用,很多算法的实现无从谈起。另一方面,指针的教学比较难,指针涉及机器硬件,不好抽象,教学中,常常要图示大量的内存图,学生往往会陷入内存细节,而忽视程序的逻辑与指针概念本身的清晰理解;
笔者在多年的教学实践中,认为应该对指针的教学几个要点作一些梳理,通过示例强调在教学中几个比较关键的知识点,同时,引入几个新概念或新的理解方式,帮助学生理解、掌握运用指针。

(一)指针变量

上述程序片段中,声明了4个变量,在教学中应该明确有关的3个问题:

1.变量与对象的关联方式

变量i保存对象2,而类型为int *的变量p保存值为i的内存地址的对象&i,我们可能更关心的是变量p与对象2的关联,应该明确,变量i与对象2是直接关联,变量p与对象2是间接关联,变量p通过保存变量i的地址而与对象2关联,可以通过间接寻址运算符*取得变量i的值(即对象2),但此时应注意,语句int * p = &i 中的符号“*”不是间接寻址运算符,其作用是编译时,告诉编译器,变量p是一个类型为int *的指针(引用)变量,变量p可以和类型为int的变量通过赋值的形式发生关联,即p = &i。

2.C语言中量(常量与变量)的分类

可以明确,C语言中,从上述变量与对象的关联方式来说,变量分为两类,一类为值类型变量,其保存的是非地址值的值对象(如变量i),另一类为引用(指针)类型变量(如变量p),其保存的值是内存地址,但要注意,*p是变量i 的别名(也就是说,在变量i的作用域范围内,凡是出现变量i的地方,均可用*p替换。),*p是左值,通过*p可以对变量i 的值进行读写,对*p进行赋值操作,等同于对变量i进行赋值操作。

对于C语言中的值常量而言,也可以分为两类,一类是地址值,我们称其为引用(指针)类型,另一类常量称为值类型。

3.指针变量的使用

C语言中,要使用指针变量,要有一个前提,指针变量要么和一个变量发生关联(如语句:int *p = &i;
使得指针变量p和变量i发生了关联),要么指针变量有一个非空的合法地址值(如语句:int*q = (int *)malloc(sizeof(int) * 2;
使得变量q得到了一个类型为int *的,由函数malloc分配的连续内存地址的首地址值)。

指针变量t被初始化为NULL,称t为空指针,t的值NULL(宏NULL在stdio.h和stdlib.h等头文件中均有定义)是一个区别于任何有效指针值的特殊值,这表明t“不指向任何地方”;
若指针变量t没有初始化,则这个指针变量就成悬空指针,这表明t是一个“不知道指向哪里”的指针,若是通过空指针或悬空指针访问内存,程序要么崩溃,要么出现难以预料的结果,为此,应该强调,一个良好的编程习惯是,将暂时不用的指针变量在声明时立即初始化为NULL,这样可以清出程序中的悬空指针,同时在使用指针变量应检测其值是否为空,如上述的代码片段中,对指针变量q的检测(if(q))。

(二)指针与数组

上述代码片段中,意在展示通对指针处理数组,学生习惯使用的arr[i],实际上,最后arr[i]被编译成为*(arr+i),也就是说arr[i]等价于*(arr+i),同理*(p+i)等价于p[i];
同时,在教学中,应该强调,p、*p以及*arr是左值,而arr不是,数组名arr是一个类型为int *const的指针常量,其值为数组arr中首元素(arr[0])的地址值,另一个问题是,arr[5]非法,而&arr[5]却合法,虽然,arr[5]不存在,但是,对arr[5]取地址是可以的,实际上,&arr[5]等价于&*(arr + 5),而&*(arr + 5)就是(arr + 5),(arr + 5)是一个类型为int *的地址值。

(三)指针、函数及结构体

上述示例代码片段中,函数total_score的作用:将结构体c的成员scores中前size-1项数组元素进行加总并返回。将类型为class*的指针作为函数的参数是一种高效的作法,这样作避免了实参向形参传值时,数组scores被复制,学生可能会疑问:设计一个函数原型为int total_score(class cl)的函数来做同样的事也可以高效完成,因为,形参cl的成员scores是一个数组名,当调用发生时,复制给形参cl的成员scores的值不就是个地址吗,实参的数组没有被复制,实参和形参共享同一个数组?应该强调的是,在C语言中,对结构体进行复制时,结构体中的数组成员也被复制,而非两个结构体共享同一个数组,因此,将类型为class结构体作为函数的参数的做法低效。

函数no_pass将结构体c数组成员中,值小于60的数组元素下标标记于另一个结构体nopass中,并返回nopass。此处,学生可能会有疑问:应该返回一个指向nopass的指针,而非结构体nopass,这样可以避免调用no_pass函数,取返回值时,未发生的结构体数组成员被复制,因而高效。在这个地方,可以强调,C语言中,返回一个指向局部自动变量的指针是致命的,一旦no_pass函数执行完毕,其所占据的内存空间随即被回收,结构体nopass不存在了,指向nopass的指针变成了指向一个不存在的对象的指针,在no_pass函数外部使用该指针,程序可能会崩溃或出现无法预知结果。因此,no_pass函数的处理方式是正确而又明智的,这种处理方式避免了“返回一个指向局部自动变量的指针”的错误。

(四)指针与字符串

上述的示例代码中,学生可能会有疑问:程序为什么报错?笔者认为,要从几个方面说起,一方面,”Hello”到底是什么?如果回答,”Hello”是字符串。那么这个回答是含混不清的。首先,”Hello”是字符串字面量(一些教材又称为字符串常量,并解译:常量就是不能被改变的量,于是修改字符串常量程序会报错,这样的解译实际上没有把问题说清楚。),字符串字面量就是字符串的在源程序中的字面表达;
其次,”Hello”被保存于一片连续的内存空间中,字符’o’的后面被追加代表字符串结束的字符’’,类型为char *的指针str的值为字符串"Hello"的第一个字符的内存地址;
再次,字符串”Hello”的类型为const char *,将一个类型为const char *的值赋给一个类型为char *的变量是可以的,因为类型char *兼容类型const char*,但是,这个const修饰符表明字符串”Hello”本身不可以修改,于是上述的示例代码中第四行会报错;
同时,应该明确,字符串”Hello”也是C语言中的表达式,其值的类型为const char *,换言之,字符串”Hello”是类型为const char *的指针。

可以明确,C语言中,字符串对象的类型是const char *,这里,可能又会有疑问:那么示例代码中,将数组名chs初始化为”Hello”,既然字符串是不可被修改的对象,那么,示例代码中的第三行为什么没报错呢?其实,chs的初始化形式仅是C语言中初始化的缩写形式,编译器会自动将字符串形式初始化转换为字符数组(在字符数组中追加字符’’)形式的初始化,于是指针常量chs指向一个字符数组对象,这个字符数组对象是一个可变对象,通过数组名chs修改数组元素的值,当然没有问题,于是,程序也就没有报错。

指针是C语言的精髓,熟练掌握指针,对于学好C语言极其重要;
同时在教学中,指针的教学是一个难点,一是指针涉及机器底层,缺乏高级程序设计语言的抽象性,学生不太容易全面而又清晰的理解与掌握指针;
另一方面,C语言中的指针与其它知识点有着紧密的联系,本文通过对C语言程序设计课程中指针的几个教学要点的梳理,明确了指针教学中的几个关键问题,以期在教学有所裨益。

猜你喜欢 字符串数组指针 JAVA稀疏矩阵算法电脑报(2022年13期)2022-04-12JAVA玩转数学之二维数组排序电脑报(2020年24期)2020-07-15郊游娃娃画报(2019年4期)2019-05-14更高效用好 Excel的数组公式电脑爱好者(2017年22期)2017-12-04为什么表的指针都按照顺时针方向转动广东第二课堂·小学(2017年9期)2017-09-28一种基于PowerBuilder环境字符串相似度算法数字技术与应用(2017年3期)2017-05-17SQL server 2008中的常见的字符串处理函数科教导刊·电子版(2016年30期)2016-12-26倍增法之后缀数组解决重复子串的问题中国新通信(2016年17期)2016-11-17最简单的排序算法(续)中国信息技术教育(2015年21期)2015-09-10寻找勾股数组的历程初中生之友·中旬刊(2015年4期)2015-06-10

推荐访问:几个 指针 要点