C语言指针教学难点透析

2017-02-25 07:10:08张忆文
计算机教育 2017年1期
关键词:数组指针指向

张忆文

(华侨大学 计算机科学与技术学院,福建 厦门 361021)

C语言指针教学难点透析

张忆文

(华侨大学 计算机科学与技术学院,福建 厦门 361021)

指针既是C语言的重点,又是教学难点。文章从指针的基本概念入手,由浅入深地讨论指针教学的重点与难点,重点介绍指向数组元素的指针、指向数组的指针、指针数组、指针函数以及函数指针变量等容易混淆的概念,通过应用实例揭示它们之间的区别,进而阐释指针的实质。

C语言;指针;函数;数组

0 引 言

C语言程序设计在计算机程序设计语言中占有重要的一席之地,它以语法简洁紧凑、程序精炼、运算符和数据结构丰富、编程灵活、可移植性好而著称[1]。然而由于教科书内容僵化、过于抽象,且授课的对象往往是大学低年级学生,造成某些知识点难以理解,甚至理解错误。其中,C语言指针教学就是公认的教学难点[2],因为指针是C语言的一大特色,用途极其广泛,所以,如何让学生透彻地理解指针,避免在使用过程中不犯错误或者少犯错误,是C语言教学中的一个重要问题。

1 指针的基本概念

1.1 指针变量的定义

所谓指针是指变量在内存中的地址,是一个常量,其实质是对地址的操作实现对数据的操作。“&”和“*”是指针的两个最基本的运算符。“&”是取地址运算符,也就是将变量在内存的地址取出来,其结合性为自右向左。“*”是取内容运算符,也就是将指针变量所指向变量的值取出,其结合性为自左向右。用来存放指针(地址)的变量成为指针变量。其定义如下:

类型标识符 *指针变量名表;

例如:

void main( ){

(1) int i=20;

(2) int *p;

(3) p=&i;

(4) printf(“i address is %x p address is %x *p=%d”,&i,&p,*p);}

语句(2)定义了指针变量p,语句(3)对指针变量p进行初始化,使它指向普通变量i。注意到语句(2)指针定义中的“*”不是指针运算符,不进行任何运算,它仅仅是标志所定义的变量为指针变量。运行程序可知, i address is 0018FF44,p address is 0018FF40,*p=20。普通变量i和指针变量p的关系如图1所示。

图1 普通变量i与指针变量p

从图1可以看出指针变量p的值为普通变量i的地址,也就是说指针变量p指向了普通变量i。普通变量i的值为20。指针变量和普通变量都是变量,其在内存中都占据一定的空间,例如:指针变量p在内存的地址为0018FF40,而普通变量i在内存的地址为0018FF44。指针变量与普通变量的最大区别就是:指针变量所存储的内容是其他变量地址,而普通变量所存储的内容是值。

1.2 指针变量的引用

指针变量的使用要注意以下几点:

(1) 必须先定义,后使用。

(2)对指针变量的操作,其类型要一致。例如int i=50;char *p=&i;这个操作就是非法的,因为普通变量i其类型是整形,而指针变量p其类型为字符型,这两个变量的类型不匹配。

(3)不能将数值直接赋值给指针变量,例如int *p=62353;这个语句也是非法的,因为指针变量只能存储其他变量的地址。

(4)指针变量在使用之前必须对其进行初始化,例如 int *p;printf(“*p is %d”);这个操作是非法的,因为没有对指针变量进行初始化,也就是说指针变量没有所指。

(5)指针变量可以进行算术运算,例如 int a[5]={0,1,2,3,4}; int *p=a; p+3;语句p+3不是指针变量p的值简单加3,而是使指针变量p指向a[3],假设一个int占用4个字节(不同的编译器,所占用的字节不一样),p+3相当于移动了12个字节。

(6)注意const int *p;int const *p; const int const *p;三者的区别。const int *p表示值不变地址可变,也就是说不能利用指针变量p对其所指向的对象的值进行修改,但指针变量p可以指向同类型的其他变量。int const *p表示地址不变,值可变,也就是说可以利用指针变量p对其所指向的对象的值进行修改,但指针变量p不能指向同类型的其他变量。const int const *p表示值不变,地址也不变,也就是说不能利用指针变量p对其所指向的对象的值进行修改,也不能使指针变量p指向同类型的其他变量。

2 指针与数组

数组是C语言中的一个重要构造类型,是具有相同数据类型的有序数据集合。数组中的每个元素都在内存中占用存储单元,且每个存储单元占据的存储空间都是相同的。数组名代表数组元素的首地址,可以将其直接赋值给相同数据类型的指针变量。在指针的教学过程中,必须注意指向数组元素的指针、指向指针的指针、指向数组的指针、指针数组等概念的区别与联系。

2.1 指向数组元素的指针

指向数组元素的指针变量的定义与以前的指针变量的定义一样,但要确保数组的类型与指针变量的类型一致。例如:

以上的两条语句定义了指向一维数组a的元素的指针变量p,并且对指针变量p进行初始化,使其指向数组的首元素a[0]。接下来我们就可以使用指针变量p访问数组元素。例如p+1指向元素a[1],注意p+1不是将p的数值简单加1,而是使其指向当前元素的下一元素。所以*(p+1)与a[1]等价。更一般化,p+i (0≤i≤4)表示指针变量p指向数组的a[i]元素,所以*(p+i)与a[i]等价。当然也可以直接用数组名直接访问数组元素,因为数组名代表数组首元素的地址,所以有*(a+i)与*(p+i)等价。

以上介绍了用指针变量访问一维数组元素,接下来介绍指针变量访问二维数组元素。例如:

想要访问二维数组中的元素a[2][3],可以用如下的方法:

(1)直接用数组下标a[2][3];

(2)用一维数组名a访问:*(a[2]+3)或*(a[0]+11)或*(a[3]-1];

(3)用二维数组名a访问,只要将(2)中的一维数组名改成相应的二维数组名即可:*(*(a+2)+3)或*(*(a+0)+11)或*(*(a+3)-1);

(4)用指针变量p访问:*(*(p+2)+3)或*(*(p+0)+11)或*(*(p+3)-1)等等。

2.2 指向指针的指针

指向指针的指针是指一个指针变量指向了另外一个指针变量,常称为多级间址。例如:

语句(1)、(2)、(3)定义了指向指针的指针变量q并且对其进行初始化,指针变量q指向了指针变量p,而指针变量p指向了普通变量i。 运行程序可知,i address is 0018FF34,p address is 0018FF30,q address is 0018FF2C,p=0018FF34,q=0018FF30,*p= 20,**q=20。普通变量i、指针变量p和指向指针的指针变量q的关系如图2所示。

图2 指向指针的指针变量关系

从图2可以看出指向指针的指针变量q的值为指针变量p的地址,也就是说指向指针的指针变量q指向了指针变量p。指针变量p的值为普通变量i的地址,也就是说指针变量p指向了普通变量i。*q指向了普通变量i,因此,**q的值就是i的值等于20。

2.3 指向数组的指针

指向数组的指针是数组名的指针,即数组首元素的指针,其实质为指针。接下来的实例将介绍指向一维数组的指针。

void main( ){

(1) int a[]={10,20,30,40,50};

(2) int (*p)[5];

(3) p=&a;

(4) printf(“%d ”, *(*p+3) );

(5) int b[2][4]={{0,1,2,3},{4,5,6,7}};

(6) printf(“%d ”, *(*(b+1)+3) );}

语句(2)声明p是一个指向一维数组的指针,且所指向的一维数组的长度为5,其实质相当于二级间址。语句(3)将p初始化,使其指向一维数组a。*p的值和a的值一样,都指向a[0],(*p+3)指向a[3],所以语句(4)的输出结果为40。注意语句(3)不可以写成p=a,因为尽管a是数组名代表数组元素的首地址,但其与数组名的地址是两回事。实际上,二维数组名就是一个指向一维数组的指针,所以语句(6)的输出结果为7。接下来的实例将介绍指向二维数组的指针。

void main( ){

(1) int a[2][3]={10,20,30,40,50,60};

(2) int (*p)[2][3];

(3) p=&a;

(4) printf(“%d ”, *(*(*p+1)+2));

语句(2)声明p是一个指向二维数组的指针,且所指向的二维数组的第一维长度为2,第二维长度为3,其实质是指向二级指针的指针,相当于三级间址。语句(3)将p初始化,使其指向二维数组a。*p的值为a,(*p+1)指向一维数组a[1],*(*p+1)+2指向二维数组元素a[1][2],所以语句(4)的输出结果为60。

2.4 指针数组

指针数组是指由若干个相同类型的指针组成的数组,其实质是数组。指针数组与普通数组的本质区别在于指针数组的元素是指针,而普通数组的元素是数值。指针数组与指向数组的指针的本质区别在于指针数组的实质是数组,而指向数组的指针其实质是指针。接下来的实例将介绍指针数组的定义及使用。

void main( ){

(1) char *p[4]={“apple”, “orange”, “pear”, “banana”};

(2) int i=2;

(3) printf(“%s ”, p[2]);}

语句(1)定义了指针数组p,它由4个元素(p[0]~p[3])组成,每个元素都是一个指向字符类型的指针。此外语句(1)还初始化了指针数组p,使p[0]的值指向字符串apple的首地址,p[1]的值指向字符串orange的首地址,p[2]的值指向字符串pear的首地址,p[3]的值指向字符串banana的首地址。特别需要注意区分char *p[4]与char (*p)[4],前者是指针数组,后者是指向一维数组的指针。语句(3)输出的结果为pear。

3 指针与函数

3.1 指针作为函数参数

整型、实型、字符型、数组、指针等都可以作为函数的参数,但整型、实型、字符型等作为函数参数时,实参与形参间是单向的值传递,也就是说形参的值发生变化不会影响到实参。而指针作为参数时,实参与形参间是传递的是地址,也就是说实参与形参共享一个地址空间,如果形参的值发生改变,实参的值也会相应的改变。例如:

上述实例中,函数void swap(int *p, int *q)是以指针作为形参,其作用是交换两个形参的值。在主函数中,定义了两个整形变量a、b并且分别赋值为10、20;此外,还定义了两个整形的指针变量pa、pb使其分别指向a和b。调用函数swap时,需要注意的是实参的类型和形参的类型必须一致,在调用函数swap之后,程序输出a=20,b=10,说明以指针作为函数参数,形参的值变化,相应的实参的值也发生改变。

3.2 指针函数

指针函数是指函数的返回值为指针类型,也就是说函数最后返回的是一个地址,而不是一个数值。指针函数的定义形式为:

类型标识符 *函数名(参数名){

函数体

以上实例中,首先定义了全局变量a,并且对其初始化,使其值为10;接下来定义了指针函数fun(),其返回类型为指向整型的指针,其参数列表为空。fun()函数的主要作用是返回全局变量a的地址。主函数首先定义了整型指针变量p,接下来对其初始化,使其指向fun()函数,接着输出p所指向对象的值,最终的运行结果为*p=10。注意到指针函数不能返回局部变量的地址,因为局部变量在程序执行完成后,其所占用的内存空间会被系统回收。

3.3 函数指针变量

一个函数在内存中的起始地址就是该函数的指针。函数指针变量是用来存储函数指针,通过函数指针变量可以调用函数。函数指针变量的定义如下:

类型标识符 (*指针变量名)(参数类型1,参数类型2,…,参数类型n);

例如:

以上实例中,首先定义了add函数,其有两个整型的参数a和b,add函数最终返回一个整型值,add函数的作用是求a与b的和。主函数首先定义了整型变量a和b,且分别对其初始化,a的值为5,b的值为10。语句int (*p_fun)(int,int);定义了函数指针变量p_fun。注意函数指针变量与指针函数的区别,int *p_fun(int,int)表示p_fun是指针函数,其返回一个指向整型的指针。区别函数指针变量与指针函数主要看这二者在定义过程中“*”号是否用圆括号()括起来,有圆括号表示函数指针变量,没有圆括号表示指针函数。接下来的语句p_fun=add;对函数指针变量进行初始化,使其指向add函数在内存中的首地址,注意到在C语言中函数名称代表函数在内存的起始地址。接下来的语句输出(*p_fun)(a,b)的值,其结果为15。注意到(*p_fun)(a,b)也可以写成p_fun(a,b)。

4 结 论

总之,在C语言的教学中要注意结合实例讲清以下几点:①指向指针的指针其实质是二级间址;②指向数组的指针其实质是指针,而指针数组其实质是数组;③指针函数其实质是指针,而函数指针其实质为函数。

[1] 刘韶涛, 潘秀霞, 应晖. C语言程序设计[M]. 北京: 清华大学出版社, 2015.

[2] 赵辉, 冯东栋. C语言中指针的教学方法研究[J]. 福建电脑, 2011, 27(4): 187-188.

(编辑:彭远红)

1672-5913(2017)01-0155-04

G642

华侨大学引进人才科研启动基金项目(2016BS104)。

张忆文,男,讲师,研究方向为绿色计算、实时调度,zyw@hqu.edu.cn。

猜你喜欢
数组指针指向
JAVA稀疏矩阵算法
电脑报(2022年13期)2022-04-12 00:32:38
科学备考新指向——不等式选讲篇
JAVA玩转数学之二维数组排序
电脑报(2020年24期)2020-07-15 06:12:41
偷指针的人
娃娃画报(2019年5期)2019-06-17 16:58:10
把准方向盘 握紧指向灯 走好创新路
传媒评论(2017年8期)2017-11-08 01:47:36
为什么表的指针都按照顺时针方向转动
寻找勾股数组的历程
基于改进Hough变换和BP网络的指针仪表识别
电测与仪表(2015年5期)2015-04-09 11:30:42
ARM Cortex—MO/MO+单片机的指针变量替换方法
VB数组在for循环中的应用
考试周刊(2012年88期)2012-04-29 04:36:47