C语言中指针的教学

2024-05-29

C语言中指针的教学(精选10篇)

C语言中指针的教学 篇1

C语言是计算机专业大学低年级学生的一门必修课程, 对学生形成程序设计思想意义重大。程序设计又是后续计算机课程的基础, 可以说C语言课程学习的好坏直接影响到学生对本专业的学习兴趣。

指针是C语言中的精华, 内容丰富、形式多样, 也是教学中的难点。掌握好指针, 可以增强学生阅读C程序的能力, 提高学习积极性。同时也让学生对“驾驭了”计算机产生一种自豪感, 为日后更进一步的学习奠定良好的基础。

对于指针内容有很多文章、教材都做了大而全的介绍, 但却忽略了指针本身的特点, 同时忽视了大学低年级学生并没有太多的专业知识, 要看懂、读懂此类大而全的内容非常困难。本来指针就难理解, 再加上一些不熟悉的计算机专业性很强的内容, 让学生更是摸不着头脑。

为了适应低年级的学生, 又突出指针的本质、精髓, 笔者在多年的教学中探索出一种方法, 即指针的二要素:地址与值。笔者把它们作了一个直观的介绍:地址就是房间的门牌号码;其值就是房间中的内容, 既直观又容易理解。使学生避免过多地去考虑内存结构等专业知识, 直接认识该指针当前是地址 (门牌号) 或是值 (房间中的内容) 。为此本文设计为一个教学案例, 学生只需分清指针二要素, 就能完整全面地读懂大部分的C程序, 从而达到较为理想的教学效果。加之教学中采取由简至繁的方法、精心设计课堂教学用例, 学生通过学习了指针的简单运用后, 初步掌握了对程序中出现的不同指针形式进行分析的办法, 增强了学习信心。也会产生好奇心, 对进一步学习探讨复杂情况下的指针也就有了兴趣。

一、变量的说明与引用

C语言要求程序中的变量——包括指针变量——必须先说明后使用, 这是让学生认识指针的第一步, C语言中常见变量可归结如下:

通过上述介绍, 使学生从符号上认识到当面对一个变量时如何进行思考, 如何分析并得出正确的结论。

二、初步认识指针变量

把握住指针变量的二要素, 即地址 (门牌号) 与值 (房间的内容) , 对下列常见指针变量出现的情况就会有清楚的认识。这里涉及指针最基本的内容, 是教学重点。

2.1 指针变量与简单变量

☆说明部分语句:

int a=10, b=20, s, t/*其中a, b, s, t为 (整数型) 简单变量*/

int*pa, *pb;/*其中pa, pb为 (整数型) 指针变量*/

☆引用部分语句:

→有关地址 (门牌号) 的语句:

赋值部分 (1) pa=&a;/*把a的地址 (门牌号) 赋给指针变量pa。这时pa就保存了简单变量a的地址 (门牌号) , 而该门牌号的房间中存的是数值10*/

(2) pa=pb;/*指针变量pb的地址 (门牌号) 赋给pa, 这样两个指针共有pb的地址 (门牌号) , p a中原来的地址 (门牌号) 丢失了*/

→有关值 (房间内容) 的语句:

赋值部分*pa=a;/*其中*pa是指针变量p a的值 (房间内容) */

运算部分 (1) a+b;/*简单变量a与b的值 (房间内容) 相加*/

(2) *pa+*pb;/*指针变量pa与pb的值 (房间内容) 相加*/

当上述语句出现在程序中时, 学生可把重点放在理解地址与值的问题上, 从而深刻掌握了指针变量与简单变量间的关系。

2.2 指针变量与一维数组

☆说明部分语句:

int a[9];/*其中*a为 (整数型) 一维数组变量, a有9个连续的门牌号*/

int*pa, *pb;/*而pa, pb为 (整数型) 指针变量*/

☆引用部分语句:

→有关地址 (门牌号) 的语句:

赋值部分pa=a;/*把a[0]的地址 (门牌号) 赋给指针变量pa。这时pa就保存了a[0]的地址 (门牌号) */

在上述赋值后, a, &a[0], pa, &pa[0]同为9个连续门牌中的第一门牌号;而a+1, &a[1], pa+1, &pa[1]同为第二个门牌号, 依此类推。

→有关值 (房间内容) 的语句:

赋值部分*pa=a[0];* (pa+1) =a[1];/*其中*pa是指针变量pa的值 (房间内容) */

运算部分 (1) a[0]+a[1];/*a的第一二个房间中的内容相加*/

(2) *pa+* (pb+1) ;/*结果同上*/

本节内容可以说是整个指针变量的基础知识, 要让学生真正学会掌握。在此对指针变量的形式做了介绍, 教学中要精心选择设计用例, 除了讲解有关内容, 更重要的是通过例子中指针的学习让学生掌握对一般指针常见形式程序的分析方法。

三、深入掌握指针变量

此应用是在前一节学习后, 学生已基本能自己独立完成有关的指针变量的分析, 提高学习难度, 一是巩固前边的学习成果, 二是通过指针来接触一点计算机的专业知识。即使这一部分的学习有困难, 也不影响学生已经掌握了的指针的基本知识。

教学中笔者采取的方法是, 通过比较, 借助上节中指针的二要素, 学生很容易便可理解C程序中一些较为复杂的指针内容。

3.1 二级指针

若有说明语句:inta=5, *pa, **pb;/*其中**pb相当于* (*pb) , 即指针的指针*/

指针的指针即是*pb中存的是pb的地址, 即**pb是存放指针类型变量的地址。

引用语句: (1) pa=&a;/*简单变量a的地址存入指针变量pa中*/

(2) pb=&pa;/*把pa的地址存入指针变量pb中*/

此时变量*pb的值是pa的地址, *pb是房间一;pa是房间二;a是房间三。房间一中存放的是房间二的门牌号, 而房间二中存放的是房间三的门牌号。所以此时输出语句:printf (“%d”, **pb) ;printf (“%d”, *pa) ;与printf (“%d”, a) ;结果相同, 都是简单变量a的值5。

注意 (2) 式不能写成pb=*pa;因为*pa是指针变量pa的值而非地址。

3.2 指针与二维数组

由于C语言定义二维数组格式为a[][], 其本质为以一维数组a[]为数组名的一维数组, 即可理解为一维数组 (a[]) []。再结合一维数组与指针变量的关系就容易理解了:

说明语句:int a[], b[][], *pa, *pb;/*说明b是二维数组*/

引用语句中:pa=a与pb=b[0]意义相同, 都是地址 (门牌号) 的赋值, 借助此关系, 再通过具体精选的C程序, 就可让学生对照一维数组的情况来处理二维数组了。

3.3 指针数组

C语言中定义了:类型*<名>[], 如Char*ch[5], 称为指针数组。意思是定义了五个指针变量:ch[0]-ch[4], 而ch[0]是若干个连续单元中第一个单元的地址, ch存放该地址 (一维数组的约定) 。

说明语句:int*p, *ch[5], a[5];

引用语句:p=ch;p=&ch[0];

ch[0]=&a[0];ch[0]=a;

讲到此可比较下述四种定义的意义:

本节重点是用对照比较的方法进行教学, 而不是脱离上节的直接讲解, 要注意设计讲解用例。

四、总结

本文对C程序中主要出现的指针变量, 分为两个教学层次, 首先对指针的核心内容——二要素——地址与值, 从指针变量出现的形式给予重点讲解, 再设计一定数量的C程序把这些指针变量的形式贯穿其中, 让学生从实践出学习体会。有了这层知识后, 再采取对照比较教学法, 让学生更多地自己体会较为复杂形式的指针变量。此时学生对此问题的学习是有思路可循的, 教学中再精心设计一些C程序用例, 通过讲练结合, 便可使学生对指针变量有一个全面而深刻的认识。

当然作为一门技能性很强, 又是培养学生程序设计思想的课程, 通过一个学期的学习全面掌握C语言, 特别是其中的指针内容, 这个不现实, 所以在教学中指针与函数的关系并未作重点介绍, 这方面需要更多的专业知识, 留有余地让学生在有了一定的基础知识后, 自己来攻克该难点。

总之, 笔者在多年的教学中认识到, 讲授指针重点在于突出指针变量的二要素, 每时每刻都意识到当前指针是值还是地址, 这样对程序中出现的指针成分, 就有了清楚的思路。同样精选C程序用例也是教学中的一个值得注意的环节。指针变量的二要素成为C语言教学的一个法宝。

参考文献

[1]谭浩强, 张基温, 唐永炎.C语言程序设计教程第二版.高等教育出版社.2002年

[2]郭继展.新编C语言程序设计.机械工业出版社.2004

C语言中指针的教学 篇2

考虑数组的指针的时候我们要同时考虑类型和维数这两个属性,换一句话,就是说一个数组排除在其中存储的数值,那么可以用类型和维数来位置表示他的种类。

A)一维数组

在c和c++中数组的指针就是数组的起始地址(也就第一个元素的地址),而且标准文档规定数组名代表数组的地址(这是地址数值层面的数组表示)。例如:

int a[10]; int *p;

p=&a[0]//和p=a是等价的:

因为a是数组名,所以他是该数组的地址,同时因为第一个元素为a[0],那么&a[0]也代表了该数组的地址。但是我们是不是就说一个数组名和该数组的第一个元素的&运算是一回事呢?在一维的时候当时是的,但是在高维的时候,我们要考虑到维数给数组带来的影响。

a[10]是一个数组,a是数组名,它是一个包含10个int类型的数组类型,不是一般的指针变量噢!(虽然标准文档规定在c++中从int[]到int*直接转换是可以的,在使用的时候似乎在函数的参数为指针的时候,我们将该数组名赋值没有任何异样),a代表数组的首地址,在数字层面和a[10]的地址一样。这样我们就可以使用指针变量以及a来操作这个数组了。

所以我们要注意以下问题:

(1) p[i]和a[i]都是代表该数组的第i+1个元素;

(2) p+i和a+i代表了第i+1个元素的地址,所以我们也可以使用 *(p+I)和*(a+I)来引用对象元素;

(3)p+1不是对于指针数量上加一,而是表示从当前的位置跳过当前指针指向类型长度的空间,对于win32的int为4byte;

B)多维数组

对于二维数组a[4][6];由于数组名代表数组的起始地址,所以a(第一层)和第一个元素a[0][0]地址的数字是相同的,但是意义却是不同的。对于该数组我们可以理解为:a的一维数组(第一层),它有四个元素a[0]、a[1]、a[2]、a[3](第二层),而每个元素又含有6个元素a[0][0],a[0][1],a[0][2],a[0][3],a[0][4],a[0][5](第三层),…到此我们终于访问到了每个元素了,这个过程我们经历了:a->a[0]->a[0][0];

整体来讲:a是一个4行5列的二维数组,a表示它指向的数组的首地址(第一个元素地址&a[0]),同时a[0]指向一行,它是这个行的名字(和该行的第一个元素的首地址相同(第一个元素为地址&a[0][0]))。所以从数字角度说:a、a[0]、&a[0][0]是相同的,但是他们所处的层次是不同的。

既然a代表二维数组,那么a+i就表示它的第i+1个元素*(a+i)的地址,而在二维数组中

*(a+i)又指向一个数组,*(a+i)+j表示这个数组的第j+1个元素的地址,所以要访问这个元素可以使用 *(*(a+i)+j)(也就是a[i][j])。

他们的示意图为(虚线代表不是实际存在的):

对照这个图,如下的一些说法都是正确的(对于a[4][6]):

a是一个数组类型,*a指向一个数组;

a+i指向一个数组;

a、*a和&a[0][0]数值相同;

a[i]+j和*(a+i)+j是同一个概念;

总结一下就是:我们对于二维指针a,他指向数组a[0,1,2,3],使用*,可以使他降级到第二层次,这样*a就指向了第一个真正的数组。对于其他的情况我们也可以采用相同的方式,对于其他维数和类型的数组我们可以采用相类似的思想,

说到指向数组的指针,我们还可以声明一个指针变量让它指向一个数组。例如:

int (*p)[5];

这时p就是一个指针,要指向一个含有5个int类型元素的数组,指向其他的就会出现问题。

这个时候我们可以使用上面的什么东西来初始化呢?

我们可以使用*a,*(a+1),a[2]等。

原因很简单:我们在一个二维的数组中,那么表达方式有上面的相互类似的意义呢?只有 *a,*(a+1),a[2]等,

C)指针数组

一个指针数组是指一个数组中的每个元素都是一个指针,例如:

int *p[10];//而不能是int (*p)[10]

或者

char *p[10];

此时p是一个指针(数值上和&p[0]一样);

在前面有int t[10];

int * pt=t;//使用pt指向t

那么这里我们用什么指向int *t[10]中的t呢?我们要使用一个指针的指针:

int **pt=t;

这是因为:在int *t[10]中,每个元素是指针,那么同时t又指向这个数组,数组上和&t[0]相同,也就是指向t[0],指向一个指针变量,可以说是一个指针的指针了,所以自然要用

int **pt;

D)指针的指针

一个指针变量内部可以存储一个值,这个值是另外一个对象的地址,所以我们说一个指针变量可以指向一个普通变量,同样这个指针变量也有一个地址,也就是说有一个东西可以指向这个指针变量,然后再通过这个指针变量指向这个对象。那么如何来指向这个指针变量呢?由于指针变量本身已经是一个指针了(右值),那么我们这里就不能用一般的指针了,需要在指针上体现出来这些特点,我们需要定义指针的指针(二重指针)。

int *p1=&i; int**p2=&p1;

综合以上的所有点,下面是我们常常看到一些匹配(也是经常出错的地方):

int a[3],b[2][3],c,*d[3]; void fun1(int *p); void fun2(int (*p)[3]); void fun3(int **p); void fun4(int p[3]); void fun5(int p[]); void fun6(int p[2][3]); void fun7(int (&p)[3]);

函数 不会产生编译时刻的可能值(但逻辑上不一定都对)

函数

不会产生编译时刻的可能值(但逻辑上不一定都对)

fun1

a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]

fun2

b,b+i,

fun3

d

fun4

a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]

fun5

a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]

fun6

C语言中指针的教学 篇3

关键词:指针;数组; C语言

中图分类号:TP311文献标识码:A文章编号:1671-864X(2015)05-0200-01

指针是C语言中的一个重要概念,也可以说是C语言的灵魂。指针的引入使C语言变得高效和灵活,同时也给使用者尤其是初学者带来一定的困惑。在教学实践中经常会发现C语言指针使用中的一些常见的具有典型性的错误,现列举分析如下。

一、间接引用未初始化的指针

对于指针变量如果仅进行了定义而未对其进行初始化,则不可对其进行间接访问。例如int *p;*p=100;,这是初学者常犯的一个错误。对于指针变量p进行定义,仅仅是为p分配了一个存储空间,而这个存储空间里所存储的值在没有对p进行初始化之前是不可预知的,而这个不可预知的值既然存储在变量p中就自然被理解为是一个地址值。在这种情况下,执行语句*p=100;就会改写以这个存储于p中的不可预知的值为地址的存储单元里的内容,而该存储单元中可能存储着一个重要的数据,这样就有可能破坏系统的运行,造成严重后果。把上述错误语句改为int *p, a;p=&a; *p=100;由于在对p进行间接引用之前已对其进行初始化,使指针p指向整型变量a,则不会出现上述错误。

二、不能正确区别指针变量与数组名的使用方法

指针变量是用来存放地址值的,而数组名代表该数组第一个元素的地址。例如:

int *p,a[]={1,2,3,4,5};其中a的值即为数组a中第一个元素的地址即&a[0],可以将指针p初始化为a(即p=a;等价于p=&a[0];),这样指针p就指向数组a的第一个元素a[0]。但是必须注意p是可以存放地址值的变量而a则是一个地址常量。请看下例:

#include

int main()

{ int i,a[]={1,2,3,4,5};

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

printf("%d\n",*(a++));

return 0;

}

该程序段不能输出数组a的元素的值,编译時会给出出错提示“'++' needs l-value”(自增运算符只能应用于左值)原因在于a是一个常量,而常量不能作为左值,即不能出现在赋值符的左侧,不能进行自增运算。这是数组名与指针变量的重要区别。

将上述错误程序改写如下,则能正确输出数组a的各个元素的值:

#include

int main()

{ int *p,i,a[]={1,2,3,4,5};

p=a;

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

printf("%d\n",*(p++));

return 0;

}

三、不能正确区别字符指针变量与字符数组的使用方法

字符指针变量用来指向一个字符而字符数组可以由若干个数组元素组成,每个数组元素都可以存储一个字符。从这一点上来看,二者并不容易混淆。但字符指针变量和字符数组都可以用字符串常量来初始化,这时就要特别注意二者的区别了。例如下面对字符指针变量和字符数组的初始化都是合法的:

char a[]=”abcdefg”; //初始化字符数组a

char * p=”abcdefg”;//初始化指针变量p

但前者是定义字符数组a并把字符串”abcdefg”中的字符逐个赋给数组a的各元素,而后者是将字符串的第一个元素的地址赋给p。虽然以上两个赋值语句的机制并不相同但利用数组a和指针p都能输出字符串”abcdefg”。下面的程序输出两次字符串”abcdefg”:

#include

int main()

{char a[]="abcdefg";//初始化字符数组a

char * p="abcdefg";//初始化指针变量p

printf("%s\n",a);//利用数组输出字符串

printf("%s\n",p);//利用指针输出字符串

return 0;

}

字符指针变量和字符数组都可以用字符串常量来初始化,并能利用指针变量和字符数组正确输出,但二者的机制却不相同。在C语言中,初始化字符指针时创建的字符串被定义为只读,不能利用指针修改这个字符串的值。而由字符串常量初始化的数组是可以修改的,数组中的元素可以改变。例如:

char a[]="abcdefg";//初始化字符数组a

char * p="abcdefg";//p指向字符串常量的第//一个字符

a[2]=’f’;//合法

p[2]=’f’;//非法,字符串常量不能改变

再看下例:

# include

void swap(char *x,char *y)

{ char t;

t=*x; *x=*y; *y=t;

}

void main( )

{ char *s1="abc",*s2="123";

swap(s1,s2); printf("%s,%s\n",s1,s2);

}

程序执行后的输出结果是()。

A)123,abc B)abc,123 C)1bc,a23 D)321,cba

这是2006年4月二级C语言试题第38题,标准答案是C。笔者认为这是一道值得商榷的题目。通过实验验证该程序可通过编译但不能正常运行,更不能得出选项C所给出的结果。原因就在于初始化字符指针s1时创建的字符串"abc"和初始化字符指针s2时创建的字符串"123"被系统定义为只读,不能利用指针修改这个字符串的值。而该程序在函数swap中试图利用指针修改字符串常量"abc",和"123"的值。若改用用字符数组s1[]和s[2]分别接收字符串"abc"和"123",运行程序后得到的输出结果为“1bc,a23”。

四、结语

程序设计语言的学习是一个在纠错中不断提高的过程,了解C指针使用中常见的典型性错误并认真分析其原因有助于提高对C语言的理解水平和应用水平。

参考文献:

[1]Peter Van Der Linden著,许波译.C专家编程[M].北京:人民邮电出版社,2008.

[2]Kenneth A. Reek著,许波译.C和指针[M].北京:人民邮电出版社,2008.

C语言教学中指针浅析 篇4

一、使用电子教室配合多媒体教学

C语言程序设计是一门实践性极强的课程, 传统的课堂教学往往使学生在听课的过程中感到极为吃力, 几乎完全不能理解教师所讲解的内容。而C语言中的指针又是C程序设计中的一个重点也是一个难点, 概念抽象, 学生更是难以理解。在我们的教学中采用将学生分组带进实验室进行实际的多媒体配合电子教室进行和教师同步教与学的方法。让教师一边演示一边讲解, 使指针的教学由枯燥、晦涩变得生动活泼起来, 加深学生对指针的理解和掌握, 比如说针对以下两个问题的教学。

1、什么是指针

指针实质是代表某一个内存单元的地址, 是内存单元的编号;在C语言的译系统中, 对于变量的访问形式之一, 就是先求出变量的地址, 然后再过地址对它进行访问, 这就是指针, 变量的指针就是指该变量所占用的内存单元的首地址。

2、什么是指针变量

指针变量是存放内存单元编号的变量, 或者说指针变量就是存放地址的变量。比如说有这样的定义:

int*i_point;;

在这里point是变量的名字, int*代表i_point这个变量存放的是int型变量的地址。再作如下定义:

int i=10

i_point=&i;

为了让学生彻底理解指针变量和指针是两个不同的概念, 教师可以制作出有趣的PPT动画进行演示。

二、指针教学中学生容易犯的错误

1、指针变量未初始化

学生在学习的过程中定义好一个指针变量后, 常常会忘记对其进行初始化操作。在C语言中, C语言的变量, 都是先定义后使用, 而C中的指针变量却有所不同, 指针变量在使用前, 不仅要先定义, 还要进行初始化也就是要对对指针进行赋值。如果没有对指针变量进行初始化, 那定义的指针变量就不具有具体的指, 将会随机指向内存单元中的某个地址, 如果不幸指向系统区中, 就会造成灾难性的后果。所以必须对指针变量进行初始化。

在这个程序中, 指针变量*point没有经过赋值操作, 就很容易造成计算机系统的灾难性崩溃!

2、对指针变量直接进行赋整数值

运行结果为:10, 20

10, 20

本程序定义了两个指针变量g1和g2, “g1=&i;和g2=&j;”语句是将i与j的地址分别赋给g1和g2, 不能写成“*g 1=&i;和*g 2=

&j;”。

与指针相关的两个运符:

(1) &:取地址运算符。

(2) *:指针运算符或称间接访问运算符, 取指针所指向的目标值。

“&”与“*”运算符的优先级别相同, 按自右而左的方向结合, 如“g1=&i;”语句, 若&*g1, 先进行的是*g 1的运算, 再执行&运算。

如:*&i的含义是什么?当然先进行&i运算, 得到i的地址, 再进行*运算。

三、指针与数组的安全性

深度理解C语言的指针与数组 篇5

Section 1 左值与右值

编译器为每个变量分配一个地址(左值),该地址在编译时可知,且变量在运行时一直存于该地址。存于该地址的变量的值(右值)只有在运行时可知。因此,编译器如果需要一个地址来执行某种操作,它可以直接进行操作,如果需要一个变量的值,它需要发出指令从指定地址中读入变量值并存于寄存器中。到这里,可以理解作为一个指针变量,它本身的地址是左值,它变量的值(即指向的地址值)为右值。所以指针首先需要在运行时取得它的当前值,然后才能对它进行解除引用操作。

数组名是一个左值,即内存中的位置。但数组名是一个不可修改的左值,即不可被赋值。

int main()

{

int a[3] = {0};

int b = 1;

a = &b; //ERROR: “=” : 左操作数必须为 l 值。

return 0;

}

Section 2 数组与指针的不同

一个例子:

int main()

{

char arr[4] = “abc”; // Note 1

//char arr[4] = {'a', 'b', 'c', '�'}; // Note 2

char *ptr = “ABC”; // Note 3

//ptr+1 = &arr[2]; // Note 4

printf(“arr: %x, %x, %x %x ”, &arr, &arr[0], &arr[1]); //Note 5

printf(“ptr: %x, %x, %x %x ”, &ptr, &ptr[0], &ptr[1]);

return 0;

}

Note 1&2等价定义,其结构如下:

a b c �

[__] [__] [__] [__]

12fed4 +1 +2 +3

Note 3结构如下

42703c A B C �

[__] [__] [__] [__] [__]

12fec8 42703c +1 +2 +3

Note 4复习一下Section 1.显然的错误,因为p+1首先需要知道p的值(右值),只有在运行时刻才能得到,编译时刻就希望对其所在的地址进行赋值显然错误,

Note 5验证Note1和3,运行结果如下:

arr: 12fed4, 12fed4, 12fed5

ptr: 12fec8, 42703c, 42703d

可以发现,arr的地址(左值)的结果与数组中首元素的地址一致,而ptr的变量值(右值)与数组的首元素地址一致。

因此对一个数组中的元素进行引用,c=arr[i]和c=ptr[i]都能够取出相应数组中的第i个元素。但要注意这两个操作的过程完全不同:

c = arr[i]; c = ptr[i];

1:取地址12fec8的内容,即42703c

1 取出i的值与12fed4相加 2:取出i的值与42703c相加

2 取地址(12fed4+ i)的内容 3:取地址(42703c+i)的内容

得到结论:尽管c=arr[i]和c=ptr[i]用同样的形式完成了同样的功能,但绝不可以混用。注意数组原始的声明方式,如果原始声明为数组式的,那么对其元素的引用要使用数组形式,反之亦然。

文件1中:

C语言中指针的教学 篇6

一、建立模型, 化抽象为具体

一般情况下, 《C语言程序设计》是计算机专业学生首先接触的一门语言, 由于大多数学生以前很少接触过编程语言, 往往对计算机语言中的语句作用理解不透, 觉得很抽象, 在前面几章内容能基本接受后, 到了指针部分的教学, 感到非常困难;确实, 指针数据类型较抽象, 但同时, C语言中指针概念又非常重要, 它可以提高程序的执行效率;书中的解释为, 指针实际上是某内存单元的地址, 根据指向对象的不同, 分不同类型的指针, 至于这个指向对象是什么, 有什么要求, 没有下文, 学生也不知是什么含义。从本章开始, 学生接受的是一种全新的数据结构——动态数据结构, 而前面所学全为静态数据结构, 而要深入理解动态数据结构, 必须先理解并掌握好指针这种数据类型。为了让学生易于理解, 我建立了一个模型, 把这个抽象的概念, 化为具体的实例图形。我举了教室与班牌之间关系的例子, 并画出实例模型, 见下图:

在这里, 教室就是一个对象, 而与该教室所对应的班牌就是指针, 这样学生基本明白, 班牌代表教室, 只是一种表示方法而已, 并且有一个指向对象, 或者可以这样理解, 班牌在某种意义上代表了教室的具体地理位置, 也就是地址, 也可以这样联想, 家所在的位置, 与家的地址联系起来。

二、循循善诱, 逐渐深入

在第一个问题解决的基础上, 接着介绍了指针数据类型的定义, 指针变量的定义, 由于在前面章节已介绍过, 变量用来保存某些类型的数据, 而指针变量里面保存的内容又是什么。我仍旧用刚才的例子, “班牌”上所刻的班级名称, 其实就代表这个教室的具体位置, 也即“教室的地址”, 这样说明, 指针变量指班牌的话, 指针变量的值即班级名称, 也即“地址”, 只不过这个地址代表的是在计算机内存中存贮单元的地址;这样一来, 学生马上明白, 指针变量有用于存放指针的变量, 同时介绍两种指针运算符:&和*, &运算符用于取得某变量的内存单元地址, *运算符用于取得指针所指向的内存单元之内容, 及指针变量的C语言定义形式:类型说明符*指针变量名;接着介绍两种变量的关系, 即指向指针的指针与指针数组, 把指针所指变量如果不是直接指向数据, 而是指向另一个指针时, 称指向指针的指针, 这要怎么理解, 如:将班的名称写在一张纸上, 并取名为班级名称, 那么, 班级名称又是一个指针, 它指向各个班牌, 而班牌又是一个指针, 它代表教室, 这样, 指向指针的指针与班名表进行类比, 指针与班牌类比, 一一对照, 不言自明。这样一步步深入, 学生在已建模型的基础上逐步领会并掌握了指针这种新的数据类型。

三、由点到面, 由表及里

在理解了指针的含义后, 再回到动态数据结构上来, 在动态数据结构中, 最基本的形式就是采用链表, 而链表就是一组节点序列, 类似于一列火车, 每个车厢即为一个节点, 通过对链表中节点的访问, 及对链表节点的插入、删除等操作, 使学生大致能理解动态数据结构的优点, 再通过一些程序举例, 就能使学生对动态数据结构进一步加深理解, 为以后程序设计打下坚实基础。以下是建立链表的例子:

综上所述, 由于计算机语言具有抽象的特点, 但同时又具有逻辑性强的特点, 所以在教学中必须对这两方面加以重视, 并注意采取引导、以学生为主的多方面的教学方法;以培养学生严密的思维能力和良好的学习习惯。

参考文献

C语言中指针的教学 篇7

1 指针概念的引入

1) 讲解变量的定义时, 引入指针的概念。

变量定义就是开辟一个指定类型长度的内存空间, 并为该空间定义一个对应的名字, 如图1所示。一个变量名对应内存单元中的一块内存空间, 而这块空间是有地址的, 对变量内容的读和写都是通过这个地址来实现的, 地址我们也称为指针。在这里我们第一次提出指针的概念。

2) 讲解一维数组时, 引入指针的概念。

定义一个一维数组就是开辟n个指定类型长度的内存空间, 每个内存空间也都和变量一样有对应的名字, 就是数组元素的名字。

例如:

int a[5];

定义一个一维数组, 包括5个元素, 其实是在内存中开辟5个整型类型长度的内存空间, 如图2所示。

a数组中的5个元素在内存中是连续存放的, 如果第一个元素a的地址是2000, 则a[1]的地址是2004 (假设一个整型占4个字节) , a[2]的地址是2008, 依此类推, a[4]的地址是2016。也就是说每一个元素都有存放该元素值的空间, 这个空间我们用地址来查找, 这里提到的地址也就是指针。在这里第二次提出指针的概念, 并强调每个变量或一维数组元素都有其对应的内存空间。

3) 讲解二维数组时, 引入指针的概念。

例如:

int a[3][4];

定义一个3行4列的一个二维数组, 其实也是在内存中开辟12个整型类型长度的内存空间, 如图3所示。

虽然二维数组a分为三行四列, 但在内存中开辟的空间是连续的, 即先为第一行开辟四个整型类型长度的内存空间, 再为第二行开辟四个整型类型长度的内存空间, 最后为第三行开辟四个整型类型长度的内存空间。也就是说为a数组连续开辟12个整型类型长度的内存空间。和整型变量一样, 每一个二维数组的元素都有对应的标识符, 就是元素的名称, 而每个元素对应的空间也可以通过地址找到, 这里说到的地址我们也称为指针。在这里第三次提出指针的概念, 并明确不管是一维数组还是二维数组中的元素, 都和普通变量一样都有其对应的存储空间。

2 明确指针概念

从前面所学到的知识知道, 每个变量和每个数组元素对应的在内存中都分配有空间, 该空间是有地址的, 这个地址就是指针。

这里明确一下指针的概念:

内存是计算机用于存储数据的存储器, 以一个字节作为存储单元, 为了便于访问, 给每个字节单元一个唯一的编号, 这些单元编号称为内存地址。如图4所示。

我们定义三个整型变量:

int i, j, k;

对应地就为这三个变量在内存里分配相应的内存空间。假设分配的空间如图4所示, i分配的是2000到2003四个字节空间, j分配的是2004到2007四个字节空间, k分配的是2008到2011四个字节空间。

定义完如上变量后, 如果有i=5;语句, 就是往i对应的空间 (2000到2003四个字节) 里赋值5。如果有printf (“%d”, i) ;语句, 就是从i对应的空间 (2000到2003四个字节) 中取出值输出。

指针就是地址。我们把定义变量时为该变量分配的起始地址称为该变量的地址。上面所定义的变量中, i的地址 (指针) 是2000, j的地址是2004, k的地址是2008。

3 明确指针变量的概念并讲解地址符和取值符号

明确了指针的概念, 下面要明确的是指针变量的概念, 指针变量是用来存放指针的, 即用来存放地址的。定义的方式如下:

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

例如:

int*p;

定义一个指针变量p, 并让它指向整型, 即该指针变量用来存放整型数据的地址。

如果有:int a;

那么p中就可以存放整型变量a的地址了, 那么如何让指针变量p存放a的地址呢?

p=&a;该语句把a的地址赋给了p, 此时我们称p指向了整型变量a。如图5所示。

在指针中有两个很重要的运算符号, 即取地址符号 (&) 和间接访问运算符号 (*) 。

1) &:取地址运算符。如:&i, 取变量i的地址。

2) *:间接访问运算符。取其后指针变量指向的变量或取其后地址中的值。如:*p, 表示取指针变量p指向的变量;*&a表示取a的地址中的值, 也就是a。

4 指向一维数组和二维数组元素的指针变量

在讲一维数组、二维数组的时侯已经强调过, 数组的每个元素都有对应的地址 (指针) , 如果我们定义了一个指针变量的话, 就可以用该指针变量指向数组中的每个元素, 也就是可以存放每个元素的地址了, 此时, 我们可以很方便地用指针变量去访问数据中的每个元素了。

5 指向一维数组的指针变量

可以定义一个普通类型的指针变量, 用来指向整型变量或整型数组元素, 我们也可以定义一个指针变量指向一个一维数组。

int (*p) [4];

该语句定义的是一个指针变量, 而这个指针变量是指向一个包括四个元素的数组的, 此数组的每个元素类型是整型。

如果有:

int a[4];

那么我们用语句p=&a;可以让p指向a数组了, 因为a就是一个包括四个元素的整型数组。

再例如:

int a[3][4];

int (*p) [4];

因为a数组中的每一个都是四个元素, 我们可以让p指向a数组中的任何一行, 如:p=&a[0];

6 总结

C语言教学中, 学生对指针的接受能力是最困难的, 如果我们从前面讲解变量的时侯开始, 有意地把指针的概念提出来, 并通过图解的方法让学生不断地体会地址的概念, 到了讲解指针这一章的时候, 就会变得非常自然。

参考文献

浅谈C语言中的指针 篇8

C语言是使用最广泛的程序设计语言之一,它以功能强大,语言简练,编程灵活等特点为广大程序设计人员所青睐,但对初学者是有一定难度的。尤其是指针,它既是C语言的一个重要特色,又是C语言的重点和难点。正确而灵活的使用指针,可有效的表示复杂的数据结构、动态分配空间、方便引用字符串和数组、在函数调用中获得多个返回值、并且可以直接处理内存地址等等。正是因为指针太灵活,一旦指针使用不当,将会导致程序出错,甚至造成系统崩溃。因此,要编写出正确高效的程序,正确理解和使用指针,以及了解指针使用的常见错误是很有必要的。

2 指针的理解和使用

2.1 指针的理解

理解语言指针的基础就是牢固树立指针就是地址的概念。明确地说,指针常量就是地址常量,指针变量就是地址变量。

我们先来看看指针常量:

1)若a是整型常量,则&a表示是a的地址,因为常量的地址在内存中是固定的,因此,是&a指针常量。

2)对于数组来说,只写数组名表示数组的首地址,而数组在内存中的位置是不能改变的,所以它也是指针常量。

3)对于字符串常量来说,直接写出某一字符串就是表示是该字符串的首地址,这是许多教科书上未能提到的。字符串常量在内存中有固定的位置,字符串常量的地址也属于指针常量。

4)对于某个函数来说,它在内存中的地址也是固定的。这样,函数的地址也属指针常量,在C语言中,函数名可以表示函数的地址。

再来看看指针变量:

指针变量就是地址变量,就是说,它是用来存放某一类变量地址的变量。有此概念以后,我们就容易理解指针数组的元素是用来存放变量地址的。同样,在处理链表、二叉树这类递归数据结构时,相应的结构中应存放其它结点的地址从而形成链接的成员变量,即该成员变量应是地址变量,必须用指针变量来刻画。

对于指针变量来说,C语言提供了“*”运算符,它表示取该变量的内容,而当指针变量是指向某一函数时,这时的“取内容”,应广义的理解,即执行相应的函数。

2.2 指针的使用

在程序中定义一个变量,C语言编译系统就会根据该变量的数据类型,为其分配相应的存储单元,类型不同所分配的存储单元的字节数也是不同的。指针变量是一种特殊类型的变量,定义一个指针类型后,C语言编译系统便会为该指针变量分配一个存储单元,用于存放相应变量的地址。使用该指针变量时,必须保证该指针变量指向一个明确的存储单元,即被赋值。例如:

使用指针时,常常涉及到两个运算符:

1)&:获取存储单元的地址

2)*:获取指针变量所指向的存储单元的内容

例如,&i表示变量i的地址,*p表示指针变量p所指向的存储单元的内容,即变量i的内容为2。

2.3 指针变量的类型

指针变量与一般变量一样,也有类型。指针变量的类型是指针所指向的数据的类型,我们也称指针变量类型为指针变量的基类型。指针变量的基类型不仅仅是指所定义指针变量的类型修饰符,同时还包含该指针变量是对多大的存储空间进行解释。例如:

int*p1,(*p2)[3],(*p3)[2][3];

其中,p1可指向1个int型的存储单元,p2可指向3个int型的存储单元,p3可指向6个

int型的存储单元。因此,p1+1是p1指针向下移动2个字节(基本整型占用2个字节),p2+1是p2指针向下移动6个字节(p2指向3个int型的存储单元,每个存储单元占用2个字节,即3×2),p3+1是p3指针向下移动12个字节(p3指向6个int型的存储单元,每个存储单元占用2个字节,即6×2)。

我们知道可以有下列语句定义指针与数组:

那么把两者结合起来会怎么样呢?

int*pa[3];

是定义成数组呢?还是定义成指针呢?回答这个问题的关键在于运算符的优先级。因[]的优先级高于*,加上括弧后定义变成

int(*(pa[3]));

所以pa究竟定义成什么,只要从内层依次往外层看就可以了。即pa是数组,由三个元素组成,每个元素是指针,是指向int型的指针。综合起来,pa是由三个指向int型的指针所构成的数组。

所以对于一个复杂的定义,我们可以按以下步骤来理解它:

1)根据优先次序,加上必要的括弧,使变量名在最内层。

2)从最内层开始依次往外层看,写上所看到的内容。

如:*—→指向×××的指针

[n]—→有n个元素的数组

()—→返回×××的函数等等

例如,对于int**pp;

1)加括弧int(*(*pp));

2)pp是指针,是指向指针的指针,该指针是指向int型的。

3)所以,pp是指向int型指针的指针。

注意,这时不要被两个*所困惑。pp也只不过是一个指针变量而已。请看下面的操作:如图1:

int i;

int*p;

int**pp;

pp=&p;/*把指向int型的指针变量p的地址放入pp所在的内存单元*/

p=&i;/*把int型变量i的地址放入p所在的内存单元*/

**pp=2;/*把整数2放入变量i所在的内存单元*/

现在我们知道,编绎系统不能区分简单指针和指向数组元素的指针。换句话说,对指针进行操作的时候,对指针指向的具体对象不能加以区分。如上面的pp是指向一个int型的指针变量的,而在下面的例子中,pp就指向了一个指针数组。如图2:

当然还有pp指向其它对象的可能。所以定义了1个指针以后,要记住它究竟是指向一个简单的对象呢?还是指向数组?这就要由编程序者自己掌握了。

再看下面的例子:

int(*pa)[3]:

按我们先前介绍的方法去理解,可知pa是一个指向由三个整型元素组成的数组的指针,通过下面的语句可以对它进行初始化。如图3:

3 指针使用中常见的错误

指针类型有其独特的特点,正确合理的使用可以提高程序的功能,但错误地理解和使用指针,将导致无法想象的结果。下面分析讨论指针使用中常犯的错误。

3.1 指针变量未赋值

指针变量在使用之前必须赋初值,这个初值应该是一个地址量。如果只是定义了一个指针变量而没有为它赋值,此时,指针变量指向的是一个不明确的存储单元。例如:

int*p;

*p=8;/*错误*/

该例中,将10赋值给p指向的存储单元,而p指向哪个存储单元是不确定的。应该改为:

这样指针p就指向了变量a,*p=10,实际上是将10赋值给变量a。或者也可以先调用C语言中的内存分配函数得到相应的存储空间,再将函数返回的首地址赋值给指针变量。即:

因此,对应指针变量一定要保证:先给指针变量赋值,然后再使用。

3.2 指针类型不匹配

在给指针变量赋值时,只能将与指针变量基类型相同的变量的地址赋值给改指针变量。例如:

例子中,指针pa的基类型为int型,指针pb的基类型为float型,只能将int型变量的地址赋值给pa,将float型变量的地址赋值给pb。应改为:

另外,基类型相同不仅仅是指类型相同,还包含系统分配给这种基类型的存储单元的容量也相同。例如:

此例中,p1=a是错误的,原因是p1与a的基类型是不同的,p1的基类型是含有1个int型的存储单元,a是二维数组名,是地址常量,它总共有2行,每行3个元素,因此它的基类型是含有3个int型的存储单元。其中,p2的基类型是含有3个int型的存储单元,p2与a的基类型是相同的,因此可以把a赋值给p2,这样就正确了。所以,在给指针变量赋值时,要考虑基类型是否匹配。

3.3 指针变量所指示的存储单元越界

当指针变量对所指示的存储单元进行操作时,应保证所操作的存储单元应该是已分配给程序的存储单元,不应该越界,否则将会导致严重的后果。例如:

第一个for循环用于给数组a的5个元素赋值,第二个for循环用于把数组a的5个元素输出,看上去好像没错,但输出的结果却是出现了问题,程序中存在严重的错误,原因是程序开始p指向数组a的首地址,当第一个for循环执行结束时,p指向数组a的末尾,因此第二个for循环开始执行时,p指针的值并不是&a[0],而是a+5,其值超过了系统给数组分配的空间,应该在第二个for循环前面加上一条语句:

p=a;

使p指针再重新指向&a[0],这样结果就正确了。

因此,在使用指针变量时,一定要注意指针变量应在系统所分配存储空间使用,不要超过系统分配的空间范围,否则结果是无法预料的。

3.4 指针函数带回的指针不存在

函数的返回值不仅可以是一般的数据类型,还可以为指针类型,这种带回的类型为指针的函数称为指针函数。对于函数带回的指针,这个指针所指示的存储单元应该是存在的,不可以不存在。例如:

max是指针函数,编写该函数的本意是由指针P带回x、y中较大数的地址,但函数max执行完毕,返回主函数main时x、y所占用的存储单元已被释放,而p带回的将是一个不存在的存储单元,这样做显然是不正确的。因此,程序应该改为:

这样修改后,max函数中的指针p带回的则是主函数main中a、b中较大数的地址,就可以正确输出了。可见指针函数中带回的指针应该是主调函数中变量的地址,不能是已释放空间的地址。

4 小结

指针是C语言的重要概念,是C语言的重要特色,它简洁、灵活、高效,但又有风险。正确理解和使用指针,避免常见错误发生,不仅要小心谨慎,还有多编程,多上机调试,弄清细节,发现错误,积累经验。

摘要:指针是C语言的一个重要概念,文章对指针基本概念和使用作了简单介绍,总结了指针在使用过程中常犯的错误,讨论了出错的原因,指出正确使用指针的方法,并阐述了C语言中的指针与数组、函数等结合起来的指针及其应用。

关键词:C语言,指针,数组,函数

参考文献

[1]谭浩强.C程序设计[M].北京:清华大学出版社,1999.

[2]冯博琴等.C语言学习指南[M].北京:北京机械工业出版社,1996.

C语言中的函数与指针 篇9

随着计算机技术的飞速发展及应用领域的扩大, 熟练掌握一门语言已变的尤为关键。C语言这门课程在计算机的基础教学中一直占有比较重要的地位, 然而要想突破C语言的学习, 对函数和指针的掌握是非常重要的, 本文将具体针对函数和指针的关系做详尽的介绍。

一、函数的有关概念

为了使程序的编写更加清晰、直观且易于修改, C语言中引用了函数。所谓函数, 就是一个程序模块, 该模块用来完成一个特定的程序功能。引用一个函数时, 需要包括对函数的定义、声明, 继而调用。在掌握函数相关概念的同时, 有以下几点需要注意:

(1) 调用函数和被调用函数

由上例可以看出, 函数A在执行的过程中包括了对函数B的调用, 则函数A称为调用函数 (调用函数B) , 而函数B被函数A调用, 称为被调用函数。

(2) 实参和形参

调用函数中定义的变量是实参, 被调用函数中定义的变量是形参。如上例, 函数A中的变量a是实参, 函数B中的变量b是形参。

(3) 实参变量和形参变量之间的独立性

实参变量和形参变量之间只存在值的传递过程, 实参变量的存储空间在调用函数中分配, 而形参变量的存储空间在被调用函数中分配, 被调用函数执行完毕后, 其所分配的存储空间被释放, 即形参变量的存储空间被释放, 它不会返回值给实参变量, 也不会参与调用函数的继续执行。例如 (实现两个数的交换) :

显然, 函数main是调用函数 (调用函数swap) , 函数swap是被调用函数。main函数中的a, b由main函数分配存储空间, 而swap函数中的a, b由swap函数分配存储空间。main函数执行到swap函数时, 调用swap函数, swap函数为其变量分配存储空间, 然后实现了swap函数中变量a, b的值交换, 执行完毕后即释放其分配变量的存储空间。继而, main函数继续执行, 但其变量a, b没有做任何改变, 即main函数不能实现a, b的交换。由上例可以看出, 若单纯的使用变量, 则被调用函数无法改变调用函数中的变量值, 即swap函数无法实现main函数中变量a, b的交换。

二、指针的有关概念

指针是C语言中功能最强大, 使用最广泛的一种数据类型, 主要用于描述存储单元的地址。通过使用指针, 可以在函数中进行传址调用。

(1) 指针变量的定义

定义指针变量的一般形式:类型标识符*变量名;其中, 变量名前的符号“*”表示将要定义的变量, 类型说明符表示该指针变量所指向数据的类型。例如:int*p1;char*p2;float*p3;

(2) 指针变量的引用

&为取地址运算符, 其一般形式为:&变量名, 例如:

int x=3, *p;p=&x;指针变量p指向变量x。

在使用x的值时, 可以直接使用x, 也可以用*p来代替使用x。此外, 指针变量一定是和它所对应的变量相互引用, 即指针变量在使用时一定要有明确的指向, 必须赋予具体的值, 否则将可能导致错误。

三、指针与函数的关系

在函数的编写过程中, 若单纯的只用变量参数, 则无法实现被调用函数改变调用函数中变量值的目的。而为了实现这一目的, 就需要函数和指针之间的结合使用。

(1) 引用指针, 可以实现调用函数和被调用函数中的指针变量共同指向调用函数中的存储单元, 从而实现被调用函数改变调用函数中变量值的目的。例如:

由上例可以看出, 在调用函数 (main函数) 中定义了变量a和指针变量p1, 被调用函数 (change函数) 中定义了指针变量p2。程序首先从main函数开始执行, 分配变量a和指针变量p1的存储单元, 此时指针变量p1指向变量a (p1=&a) 。当程序执行到change函数时, 程序跳转到change函数执行其函数体, change函数为其指针变量p2分配存储单元, 同时p2也得到了p1传过来的值 (变量a的地址) , 此时p2也指向了调用函数中的变量a, 即实现了p1和p2共同指向了调用函数中的存储单元 (变量a的存储单元) , change函数执行其函数体 (*p2=3) , 即使a的值变为3。change函数执行完毕后, 释放其变量的存储空间, 转而继续执行main函数, 此时a的值已经发生改变 (由2变为3) , 实现了被调用函数改变调用函数中变量值的目的。

(2) 调用函数和被调用函数中实参和形参之间的关系图

被调用函数执行完毕后, 释放它所分配的存储单元, 而调用函数分配的存储单元仍继续使用。此外, 只有当调用函数中传指针值 (即实参是指针值) , 而被调用函数中引用变量 (即形参收到指针值后, 在函数体内引用变量值) , 才能达到改变的目的。

例如, 实现变量a和b的交换, 程序如下:

例1虽然传的是指针值, 但在函数体的执行过程中引用的仍然是指针值 (引用x和y) , 所以不能实现a和b的交换;例2程序传指针值后, 引用变量 (*x和*y, 即a和b) , 所以能实现a和b的交换。

四、结束语

在以后的编程过程中, 若遇到想通过被调用函数改变调用函数中变量值的目的, 则可以把该变量的地址值传给被调用函数, 从而达到改变的目的。鉴于文章篇幅及个人能力有限, 本文肯定还存在许多不足之处, 仅供大家学习和参考。

参考文献

[1]谭浩强.C程序设计[M].二版.清华大学出版社, 2004.

[2]杜友福.C语言程序设计[M].二版.科学出版社, 2007.

C语言中指针和数组区别的分析 篇10

关键词:指针,数组,C语言

对于C语言编程新手来说, 经常认为"数组和指针是相同的"。其实这种说法是不完全正确的, 他们是有区别的。ANSIC标准6.542里建议:

注意下列声明的区别:

extern int*x;

extern int y[];

第一条语句声明x是一个int型的指针, 第二条语句声明y是int型, 数组长度尚未确定, 其存储在别处定义。

标准里并没有做更细的规定, 许多C语言书籍对数组和指针何时相同、何时不同也是含糊其辞一带而过。为了更好地理解指着与数组, 在这里我们谈谈他们之间的区别。

一、数组和指针的不同之处

我们来看以下代码:

程序编译时出错, 在这里, 数组a[100]与*a是两种不同的概念, 以上的错误相当于把浮点型数据和整型数据混为一谈, 如:

从上面, 我们可以很明显地看出int和char类型不匹配, 同理指针和数组两种不同类型也是不匹配。下面我们来谈谈是数组与指针的区别:

1. 指针和数组保存数据的内容不同

在C语言中, 我把地址形象地称为"指针", 把存放地址的变量称为指针变量, 通常我们把指针变量简称为指针, 所以指针里存放的是数据的地址, 而数组里存放的是数据的值。

2. 数组和指针的访问方式不同

数组采用的是直接访问方式, 而指针采用的是间接访问方式。

如:char a[6]="China"; c=a[3];

程序编译时, 给数组分配内存, 如图:

假设编译器符号表给的一个地址是2000程序运行时, 取3的值, 将它与2000相加, 取得 (2000+3) 的内容。在这里, 每个符号的地址在编译时可知。因此, 如果编译器需要一个地址来执行某个操作得花, 它就可以直接地进行操作, 并不需要增加指令取得具体的地址。相反, 对于指针, 它必须首先在运行时取得它的当前值, 然后才能对它进行解除操作。

如:char*p="China"; c=p[3];

编译器编译时, 编译器符号表给了一个p, 其地址假设为3000, 如图:

运行时, 取得地址3000的内容, 即5000, 后与3相加, 最后取 (5000+3) 的内容。

数组和指针的不同点还在于:指针通常由于动态数据结构, 而数组通常用于存储固定数目且数据类型相同的元素;指针用malloc () 、free () 函数来动态分配空间或释放空间, 而数组则隐式分配和删除;指针通常指向匿名数据, 而数组自身即为数据名;等。

二、数组和指针的相同之处

数组和指针什么时候相同呢?

1."作为函数参数的数组名"等同于指针

The C Programming Language, 第二版, Kernighan&Ritchie, 第99页里指出:

意思是:作为函数参数定义的形式参数, char s[]和char*s是一样的。

在函数形参定义这个特殊的情况下, 编译器必须把数组形式改写成指向数组第一个元素的指针形式。在这里, 编译器只向函数传递数组的地址, 而不是整个数组的拷贝。因此以下几种:

是一样的。

所以主函数的参数中char**argv和char argv[][]可以相互替换。

2."表达式中的数组名"就是指针, 数组下标作为指针的偏移量

假设我们声明:

int a[10], *p, i=1;

就可以通过以下任何一种方式访问a[i];

p=a;p[i];

或p=a;* (p+i) ;

或p=a+I;*p;

实际上, 我们还可以采用其它更多的方法。对于数组的引用如a[i]在编译时总是被编译器改写成* (a+i) 的形式。C语言标准要求编译器必须具备这个概念性的行为。我们可以这么记着:方括号[]表示一个取下标操作符, 就像减号表示一个减法运算符一样。取下标操作符就像取一个整数和一个指向类型X的指针, 所产生的结果类型是X, 一个在表达式中的数组名于是就成了指针。

在表达式中, 指针和数组是可以相互替换的, 因为他们在编译器里的最终形式都是指针, 并且都可以进行取下标操作。编译器可以自动把下标值的步长调整到数组的大小, 如:long型数据的长度是4个字节, 那么m[i]和m[i+1]在内存中的距离就是4, 而不是1。在对起始地址执行加法操作之前, 编译器会自动负责计算机每次增加的步长。这就是为什么指针总有类型限制的原因, 每个指针只能指向一种类型的原因是:因为编译器需要知道对指针进行解除引用操作时应该取多少个字节, 和每个下标的步长应取多少个字节。

三、总结

总而言之, 当声明时 (除作为函数参数除外) , 指针和数组是不能替换的, 如:"int a[10];", 只能用"extern a[10];", 而不能使用"extern*a;", 又如"int a[10];"和"in*a;"意义也是不同的;当作为函数参数或应用时, 两者是相同的, 如:"a[i]=5;"等同于"* (a+i) =5;", 又如, 主函数的参数中char**argv和char argv[][]可以相互替换。

参考文献

[1]谭浩强, C程序设计 (第三版) [M], 北京:清华大学出版社, 2005

[2]E.Balagurusamy, 标准C程序设计 (第4版) [M], 北京:清华大学出版社, 2008

上一篇:《天行者》下一篇:建筑施工企业安全文化