论指针中需要认真辨识的3组概念

2020-10-21 05:40赖玲
赤峰学院学报·自然科学版 2020年3期
关键词:数组存储空间指针

赖玲

摘 要:通过对指针中3组容易混淆的概念研究,介绍了它们的辨识方法,以具体实例为依据,介绍了对数据加工和处理的方法,有助于夯实编程基础,促进编程能力的提高.

关键词:指针;地址;数组;函数

中图分类号:TP312  文献标识码:A  文章编号:1673-260X(2020)03-0022-03

指针是C语言中的一个重要概念,是其最具特色的部分,是C语言的精华,同时也是C语言的难点.指针除了能动态地分配地址,表示复杂的数据结构,在调用函数的时候能够得到1个以上的结果外,在系统软件的设计也是非常重要的.笔者在实际的教学和学生的实际编程指导中发现,很多学生对“指针变量的定义符”和“指针变量的取值运算符”“指针函数”和“函数指针”“指针数组与数组指针”等概念混淆不清;认真辨识有关概念,砺清它们间的相互关系,对于编制简洁、紧凑、高效的应用程序非常有用,下面谈谈应该辨识的几个概念.

1 “指针变量的定义符”和“指针变量的取值运算符”

操作符“*”既可以用来作为指针变量的定义符,也可以用作指针变量的取值运算符.要根据“*”操作符出现的位置进行判断.

例1 int i=3, *p;

p=&i;

printf("%d",*p);

scanf("%d",p);

*p=5;

程序的第一句“int i=3;*p;”定義了一个整型变量i和一个整型的指针变量p,i里面只能存放整型变量的值,p(这里P前面的操作符“*”就是指针变量的定义符)里面只能存放整型变量的地址,i的初始值为3,执行“p=&i;”中的“&”是取地址操作符,“p=&i;”将i的地址存到指针变量p的空间里面,也可以理解为将指针变量p指向整型变量i的首地址,见图1所示.

程序中的第三句,以十进制整数的形式输出*p,对于*p理解十分重要,这里p前面的操作符“*”就是取值运算符,就是p当前所指向变量i的存储空间里面存放的内容3.第四句是向指针变量p所指向的空间输入十进制整型数据,等价于scanf("%d",&a),第五句是用5覆盖指针变量p所指存储空间里面的内容,等价于“i=5;”,因为p是指向整型变量i的首地址,“*p=5;”就是将p当前所指向变量i的存储空间里面的内容修改为5,这是间接寻址方式.

这里辨识的原则:一级指针变量中存放目标变量的地址,凡是在声明的语句中出现的操作符“*”是指针变量定义符,如例1程序第1行中的操作符“*”;在操作和控制部分出现的操作符“*”就是取值运算符,如例1程序第3行和第5行中的操作符“*”.

对于取值的理解一定要注意取值的内容,对于一级指针,取得内容为“指针所指向存储空间里面的内容”即存储空间的值,对于二级指针或者多级指针有“取存储空间的地址”和“取存储空间里面的内容”的区别.

例2 int a,*p,**pp;

a=22;

p=&a;

pp=&p;

printf("%p,%d",*pp, **pp);

假设变量a的地址为4000,指针p的地址为4100,二级指针pp的地址为4800,a、p、pp三者的关系如图2所示.

由图2可以看出,a的地址为4000,保存在指针变量p中,p指向a,p的地址为4100,保存在pp中,即二级指针pp指向指针变量p,此时,要引用a的值,可以使用*p,也可以使用**pp.注意:虽然p、pp都是指针变量,但p和pp指向的内容有着本质上的不同,p指向的是普通变量的地址,pp只能指向指针变量的地址而不能指向普通变量的地址.

也就是说,二级指针的指针变量中存放的是一级指针变量的地址,对于指针变量的定义符和一级指针一样,只要是在声明语句中出现的操作符“*”都是指针变量的定义符,例2中第1行中的操作符“*”和操作符“**”都是指针变量的定义符,第5行“printf("%p,%d",*pp,**pp);”中“*pp”的“*”和“**pp”前面的“*”是指针变量取值运算符,只不过它们取值的内容不一样,“*pp”取的是目标对象的地址,“**pp”也就是“*(*pp)”,取的是目标对象的内容.

2 “指针数组”与“数组指针”

2.1 “指针数组”

对于给定的一个数组,其元素都是存放同一类型数据的地址,这样的数组称为“指针数组”. “指针数组”其本质为数组,只不过数组元素是同一类型数据的地址.例如:

int *p[4];

这里定义了一个指针数组,该数组的每个元素是整形变量的地址.对于“*p[4]”可以这样理解,由于“[ ]”操作符比“*”操作符的优先级高,因此p先与操作符“[ ]”结合,形成p[4]形式,这显然是数组的形式,表示p数组里面有4个元素,p[4]中的p再与前面的操作符“*”结合,“*”是指针类型定义符,表示此数组是个指针类型,每个数组元素都可以指向一个整型变量.*p[i]相当于*(p[i]),都表示下标为i元素的地址,因为操作符“[ ]”优先级高于操作符“*”.

注意:这里提到的优先级,操作符“()”高于操作符“[ ]”高于操作符“*”.

2.2 “数组指针”

“数组指针”也称为行指针,是指向数组地址的指针,其本质为指针.其定义形式为:

(*指针变量名)[长度]

例如:int (*P)[n]

这里操作符“()”的优先级比操作符“[ ]”优先级高,“(*P)”表示是个指针,再与操作符“[ ]”结合,表示“数组指针”,“长度”表示二维数组分解为多个一维数组时,一维数组的长度,也就是二维数组的列数.

例3

main()

{int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

int *q,(*p)[4];

for(p=a;p

{ for(q=p[0];q

printf("%5d",*q);

}

}

本程序的目的是输出二维数组的各元素,使用了普通的指针变量和二维数组的行指针,行指针指向二维数组的首地址(p=a),p++跳过的是一行,而不是一个元素.

两者的区别:数组指针只是一个指针变量,占有目标对象一个地址的存储空间;指针数组是多个指针变量,以数组的形式存储在内存当中,占有多个指针的存储空间.

3 “指针函数”与“函数指针”

3.1 指针函数

指针函数是指返回值是指针的函数,即本质是一个函数.函数返回值是某一类型的指针.其定义形式为:

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

int *f(x,y);

首先它是一个函数,这个函数的返回值是一个地址值.

3.2 函数指针

如果在程序中定义了一个函数,编译器在程序编译之后,会为每个函数分配一个入口地址,即该函数第一条指令的地址,这个地址就是函數的指针.函数指针是指向函数的指针变量,其本质是一个指针变量.

int (*f) (int x); /*声明一个函数指针*/

f=func; /*将func函数的首地址赋给指针f */

一般情况下,我们可以用一个指针来保存这个地址,而这个指针就是函数指针,该指针可以看作是它指向函数的别名,所以我们可以用该指针来调用这个函数.

例4 用函数指针变量作函数参数实例,从键盘任意输入两个整数,求其最大值.

include"stdio.h"

int max (int x,int y)

{

return x>=y?x:y;

}

void main()

{

int a,b,c;

int (*p)(int x,int y);

scanf("%d,%d",&a,&b);

p=max;

c=(*p)(a,b);

printf("a=%d,b=%d,max=%d\n",a,b,c);

}

程序中max (int x,int y)是已经定义的函数,用来求两个数的最大值.函数int (*p)(int x,int y)中p先与操作符“*”结合,是指针变量,然后再与后面的操作符“()”结合,表示此指针变量指向函数,这个函数的返回值是个整数.

程序中的第11行“p=max;”将指针变量p指向了“max (int x,int y)”函数的入口地址,即p指向函数max ( ).a,b的值分别传递给x,y.函数的返回值是a,b的最大值.

必须注意:(1)一个指向函数的指针必须确保该函数被定义且分配了内存,否则它将指向一个空地址,这是程序设计的大忌!

(2)要注意“int (*p)(int x,int y);”中第一个括号的位置.如果写成 “int *p (int x,int y);”情形,由于操作符“( )”高于操作符“*”,这就不是一个指向函数的指针了,它就成了声明声明一个函数,这个函数的返回值一个int类型的地址.

(3)给函数的指针变量赋值时只赋函数名,不允许带参数.如第11行“p=max;”.

(4)发生函数调用的时候可以通过函数名,也可以通过函数的指针,本例中的调用形式“c=(*p)(a,b);”用函数指针调用的参数与函数调用的参数完全一致,用函数指针变量调用函数时,只需要将“(*p)”代替函数名.

(5)对指向函数的指针变量不允许做加减运算,如:p++、p—、p+n都是错误的.

4 使用指针,一定要注意指针当前的指向

使用指针可以方便访问内存,具有很强的灵活性,但在设计程序的时候,一定要留意指针当前的指向.下面的程序企图利用指针输出数组中的元素.

例5 利用指针知识给数组元素赋值,再将数组中的各元素输出.

main()

{ int i,*p,a[7];

p=a;

for(i=0;i<7;i++)

scanf("%d",p++);

printf("\n");

for(i=0;i<7;i++,p++)

printf("%d",*p);

}

程序中的第一个for语句实现了对数组元素赋值的功能,for结束的时候,执行“p++”,指针已经指向数组以后的空间.执行第二个for语句,由于p所指的空间不是数组a的起始地址,执行目标地址取值运算符“*p”时,就无法和数组元素建立联系,所以,就实现不了项目所要求的具体功能,为了解决这个问题,必须在程序的第6行后添加“p=a;”指令,使指针变量p重新指向数组a的首地址,才能实现项目所要求的具体功能.

5 结束语

程序设计中涉及的概念很多,对数据加工和处理的方法也灵活多变,只有认真厘清知识间的相互关系,认真辨析指针中常用的有关概念,注意指针当前的指向,脚踏实地认认真真的书写每行程序,才是编写出符合要求的程序.

参考文献:

〔1〕任正云,李素若.C语言程序设计(第三版)[M].北京:中国水利水电出版社,2016.

〔2〕谭浩强.C语言程序设计教程(第3版)[M].北京:高等教育出版社,2006.

〔3〕徐士良,等.C语言程序设计教程(第三版)[M].北京:人民邮电出版社,2009.

〔4〕[美]K.N.King著,吕秀峰,黄倩译.C语言程序设计:现代方法(第2版)[M].北京:人民邮电出版社,2010.

〔5〕[美]Stephen Prata著,云巅工作室译.C Primer Plus(第五版)中文版【M】.北京:人民邮电出版社,2005.

〔6〕刘军.C程序设计教学探讨[J].电脑知识与技术,2011(06).

猜你喜欢
数组存储空间指针
JAVA稀疏矩阵算法
苹果订阅捆绑服务Apple One正式上线
JAVA玩转数学之二维数组排序
用了就回不去的APP
用好Windows 10保留的存储空间
郊游
更高效用好 Excel的数组公式
为什么表的指针都按照顺时针方向转动
寻找勾股数组的历程
浅析C语言指针