浅析C程序设计中指针的奥秘

2010-09-12 05:16
长治学院学报 2010年2期
关键词:数组指针C语言

李 瑛

(太原师范学院 计算机系,山西 太原 030001)

浅析C程序设计中指针的奥秘

李 瑛

(太原师范学院 计算机系,山西 太原 030001)

在C语言中,指针是广泛使用的一种数据类型,起着不可低估的作用,同时它也是C语言的一个精华,极大地丰富了C语言的功能。文章结合作者的教学实践经验,针对学生在学习C语言指针时遇到的问题进行总结和归纳。便于为以后的教学提供参考。

指针;变量;地址;C语言

在C语言中,指针是广泛使用的一种数据类型,起着不可低估的作用,同时它也是C语言的一个精华。有人说,没有学过指针就没有学过C语言,可见,指针在C语言中作用是不可替代的。利用指针变量可以表示各种数据结构;能方便地使用数组和字符串;并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。在C语言程序设计中,指针极大地丰富了C语言的功能。能否正确理解和使用指针是我们是否掌握C语言的一个标志。但是由于指针太过灵活,一旦使用不当,不仅错误难查,而且会出现意想不到的错误。因此,要编出正确而高效的程序必须正确地理解和使用指针。针对初学者对C语言指针的理解不够透彻,作者根据在C语言教学和使用方面的经验,对指针做了详细的阐述。

1 指针的含义

指针是一种特殊的变量,它里面存储的数值被解释成为内存里的一个地址。对指针的理解需要搞清四方面的内容:指针的类型;指针所指向的类型;指针的值或者叫指针所指向的内存区;指针本身所占据的内存区。

1.1 指针的类型

从语法的角度看,只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例中指针的类型:

(1)int*pointer; //指针的类型是 int*

(2)int(*pointer)[3];//指针的类型是int(*)[3]

1.2 指针所指向的类型

当通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。从语法上看,只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:

(1)int*pointer; //指针所指向的类型是int

(2)int(*pointer)[3];//指针所指向的的类型是int()[3]

指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。对C越来越熟悉时就会发现,把与指针搅和在一起的“类型”这个概念分成“指针的类型”和“指针所指向的类型”两个概念,是精通指针的关键点之一。

1.3 指针的值

指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。以后,如果说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。

指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在上面的例子中,指针所指向的类型已经有了,但由于指针还未初始化,因此它所指向的内存区是不存在的,或者说是无意义的。所以,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?

1.4 指针本身所占的内存区

利用sizeof(指针的类型)函数,可以测试指针本身所占内存的大小,在16位平台里,指针本身占据了2个字节的长度。

2 指针运算

指针是可以进行的算术运算的。指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的。例如:

int a;int*pointer=a;pointer++;

在上例中,指针pointer的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。接下来的语句中,指针pointer被加了1,编译器是这样处理的:它把指针pointer的值加上了sizeof(int)(在16位程序中,是被加上了2),由于地址是用字节做单位的,故pointer所指向的地址由原来的变量a的地址向高地址方向增加了2个字节。

用一个指针和一个循环来遍历一个数组,例:int array[20];

这个例子将整型数组中各个元素的值加1。由于每次循环都将指针pointer加1所以每次循环都能访问数组的下一个元素。

所以,一个指针pointerold加上一个整数n后,结果是一个新的指针pointernew,pointernew的类型和pointerold的类型相同,pointernew所指向的类型和pointerold所指向的类型也相同。pointernew的值将比pointerold的值增加了n乘sizeof(pointerold所指向的类型)个字节。就是说,pointernew所指向的内存区将比pointerold所指向的内存区向高地址方向移动了n乘sizeof(pointerold所指向的类型)个字节。一个指针pointerold减去一个整数n后,结果是一个新的指针pointernew,pointernew的类型和pointerold的类型相同,pointernew所指向的类型和pointerold所指向的类型也相同。pointernew的值将比pointerold的值减少了n乘sizeof(pointerold所指向的类型)个字节,就是说,pointernew所指向的内存区将比pointerold所指向的内存区向低地址方向移动了n乘sizeof(pointerold所指向的类型)个字节。

3 运算符&和*

&是取地址运算符。若a是一个变量,&a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址,那就是a的地址。

*p的结果是p所指向的内容,并且具有如下特点:它的类型是p指向的类型,它所占用的地址是p所指向的地址。例:

int a=12;int b;int*p;int**pointer;

p=&a;//&a的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a的地址。

*p=24;//*p的结果,在这里它的类型是int,它所占用的地址是p所指向的地址,显然,*p就是变量a。

*pointer=&b;//*pointer是个指针,&b 的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以用&b来给*pointer赋值就是毫无问题的了。

**pointer=34; //*pointer的结果是 pointer所指向的东西,在这里是一个指针,对这个指针再做一次*运算,结果就是一个int类型的变量。

4 数组和指针的关系

数组的数组名其实可以看作一个指针。如下例:

int array[10]={0,1,2,3,4,5,6,7,8,9},value;

value=array[0];//也可写成:value=*array;

value=array[3];//也可写成:value=*(array+3);

2.2 坚持以人为本的原则,分层对待员工的各自情况。国有企业职工出身结构的多样性,往往体现在职工的年龄、学历、经历、性别,家庭都是造成不同的思想状态的因素,对职工进行同类分类,单独为同类职工制定相关的教育辅导方式,能够解除职工的一些顾虑,真实准确、及时地了解员工的思想动态,化解企业内部的矛盾,营造和谐融洽的企业工作环境,提高企业的生产经营效率。

上例中,一般而言数组名array代表数组本身,类型是int[10],但如果把array看做指针的话,它指向数组的第0个单元,类型是int*,所指向的类型是数组单元的类型,即int。因此*array等于0就一点也不奇怪了。同理,array+3是一个指向数组第3,所以*(array+3)等于3。其它依此类推。

声明了一个数组TYPE array[n],则数组名称array就有了两重含义:第一,它代表整个数组,它的类型是TYPE[n];第二,它是一个指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0号单元,该指针自己占有单独的内存区。注意它和数组第0号单元占据的内存区是不同的。该指针的值是不能修改的,即类似array++的表达式是错误的。在不同的表达式中数组名array可以扮演不同的角色。在表达式sizeof(array)中,数组名array代表数组本身,故这时sizeof函数测出的是整个数组的大小。在表达式*array中,array扮演的是指针,因此这个表达式的结果就是数组第0号单元的值。sizeof(*array)测出的是数组单元的大小。表达式array+n(其中 n=0,1,2,....。)中,array扮演的是指针,故array+n的结果是一个指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组第n号单元。故sizeof(array+n)测出的是指针类型的大小。

5 指针类型转换

当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。在前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的。例:

float f=12.3;float*fpointer=&f;int*p;

在上面的例子中,假如想让指针p指向实数f,那么p=&f是错误的。因为指针p的类型是int*,它指向的类型是int。表达式&f的结果是一个指针,指针的类型是float*,它指向的类型是float。两者不一致,直接赋值的方法是不行的。为了达到目的,需要进行“强制类型转换”:p=(int*)&f;如果有一个指针p,需要把它的类型和所指向的类型改为TYEP*TYPE,那么语法格式是:(TYPE*)p;

这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都没有被修改。

6 指针的安全问题

看下面的例子:

char s='a'; int*pointer; pointer=(int*)&s;*pointer=1298;

指针pointer是一个int*类型的指针,它指向的类型是int。它指向的地址就是s的首地址。在32位程序中,s占一个字节,int类型占四个字节。最后一条语句不但改变了s所占的一个字节,还把和s相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。

如下例:

1、char a;

2、int*pointer=&a;

3、pointer++;

4、*pointer=115;

该例子完全可以通过编译,并能执行。但是第3句对指针pointer进行自加1运算后,pointer指向了和整形变量a相邻的高地址方向的一块存储区。这块存储区里的具体内容并不明确,有可能它是一个非常重要的数据,甚至可能是一条代码。而第4句竟然往这片存储区里写入一个数据!这是严重的错误。所以在使用指针时,程序员心里必须非常清楚指针的指向。在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。

[1]王敬华,等.C 语言程序设计教程[M].北京:清华大学出版社.2006.264-380.

[2]谭浩强.C语言程序设计[M].北京:清华大学出版社,1995,157-212.

[3]吴斌.C语言指针的教学[J].安徽职业技术学院学报.2004,(9):67-69.

(责任编辑 李学斌)

TP312

A

1673-2014(2010)02-0026-03

2010—04—02

李 瑛(1973— ),女,河北保定人,硕士,讲师,主要从事计算机人工智能研究。

猜你喜欢
数组指针C语言
JAVA稀疏矩阵算法
基于Visual Studio Code的C语言程序设计实践教学探索
JAVA玩转数学之二维数组排序
基于C语言的计算机软件编程
为什么表的指针都按照顺时针方向转动
Excel数组公式在林业多条件求和中的应用
高职高专院校C语言程序设计教学改革探索
寻找勾股数组的历程
基于改进Hough变换和BP网络的指针仪表识别
论子函数在C语言数据格式输出中的应用