C语言中的指针详解_第1页
C语言中的指针详解_第2页
C语言中的指针详解_第3页
C语言中的指针详解_第4页
C语言中的指针详解_第5页
全文预览已结束

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、C语言中的指针详解一、数组的指针、指针数组以及指向指针的指针考虑数组的指针的时候我们要同时考虑类型和维数这两个属性。换一句话,就是说一个数组排除在其中存储的数值,那么可以用类型和维数来位置表示他的种类。A)一维数组在c和c+中数组的指针就是数组的起始地址(也就第一个元素的地址),而且标准文档规定数组名代表数组的地址(这是地址数值层面的数组表示)。例如:int a10; int *p;p=&a0/和p=a是等价的:因为a是数组名,所以他是该数组的地址,同时因为第一个元素为a0,那么&a0也代表了该数组的地址。但是我们是不是就说一个数组名和该数组的第一个元素的&运算是一回事

2、呢?在一维的时候当时是的,但是在高维的时候,我们要考虑到维数给数组带来的影响。a10是一个数组,a是数组名,它是一个包含10个int类型的数组类型,不是一般的指针变量噢!(虽然标准文档规定在c+中从int到int*直接转换是可以的,在使用的时候似乎在函数的参数为指针的时候,我们将该数组名赋值没有任何异样),a代表数组的首地址,在数字层面和a10的地址一样。这样我们就可以使用指针变量以及a来操作这个数组了。所以我们要注意以下问题:(1) pi和ai都是代表该数组的第i+1个元素;(2) p+i和a+i代表了第i+1个元素的地址,所以我们也可以使用 *(p+I)和*(a+I)来引用对象元素;(3)

3、p+1不是对于指针数量上加一,而是表示从当前的位置跳过当前指针指向类型长度的空间,对于win32的int为4byte;B)多维数组对于二维数组a46;由于数组名代表数组的起始地址,所以a(第一层)和第一个元素a00地址的数字是相同的,但是意义却是不同的。对于该数组我们可以理解为:a的一维数组(第一层),它有四个元素a0、a1、a2、a3(第二层),而每个元素又含有6个元素a00,a01,a02,a03,a04,a05(第三层),到此我们终于访问到了每个元素了,这个过程我们经历了:a->a0->a00; 整体来讲:a是一个4行5列的二维数组,a表示它指向的数组的首地址(第一个元素地址

4、&a0),同时a0指向一行,它是这个行的名字(和该行的第一个元素的首地址相同(第一个元素为地址&a00)。所以从数字角度说:a、a0、&a00是相同的,但是他们所处的层次是不同的。既然a代表二维数组,那么a+i就表示它的第i+1个元素*(a+i)的地址,而在二维数组中*(a+i)又指向一个数组,*(a+i)+j表示这个数组的第j+1个元素的地址,所以要访问这个元素可以使用*(*(a+i)+j)(也就是aij)。他们的示意图为(虚线代表不是实际存在的):对照这个图,如下的一些说法都是正确的(对于a46):a是一个数组类型,*a指向一个数组;a+i指向一个数组;a、*a和&

5、amp;a00数值相同;ai+j和*(a+i)+j是同一个概念;总结一下就是:我们对于二维指针a,他指向数组a0,1,2,3,使用*,可以使他降级到第二层次,这样*a就指向了第一个真正的数组。对于其他的情况我们也可以采用相同的方式,对于其他维数和类型的数组我们可以采用相类似的思想。说到指向数组的指针,我们还可以声明一个指针变量让它指向一个数组。例如:int (*p)5;这时p就是一个指针,要指向一个含有5个int类型元素的数组,指向其他的就会出现问题。这个时候我们可以使用上面的什么东西来初始化呢?我们可以使用*a,*(a+1),a2等。原因很简单:我们在一个二维的数组中,那么表达方式有上面的相

6、互类似的意义呢?只有 *a,*(a+1),a2等,C)指针数组一个指针数组是指一个数组中的每个元素都是一个指针,例如:int *p10;/而不能是int (*p)10或者char *p10;此时p是一个指针(数值上和&p0一样);在前面有int t10;int * pt=t;/使用pt指向t那么这里我们用什么指向int *t10中的t呢?我们要使用一个指针的指针:int *pt=t;这是因为:在int *t10中,每个元素是指针,那么同时t又指向这个数组,数组上和&t0相同,也就是指向t0,指向一个指针变量,可以说是一个指针的指针了,所以自然要用int *pt;D)指针的指针一

7、个指针变量内部可以存储一个值,这个值是另外一个对象的地址,所以我们说一个指针变量可以指向一个普通变量,同样这个指针变量也有一个地址,也就是说有一个东西可以指向这个指针变量,然后再通过这个指针变量指向这个对象。那么如何来指向这个指针变量呢?由于指针变量本身已经是一个指针了(右值),那么我们这里就不能用一般的指针了,需要在指针上体现出来这些特点,我们需要定义指针的指针(二重指针)。int *p1=&i; int*p2=&p1;综合以上的所有点,下面是我们常常看到一些匹配(也是经常出错的地方):int a3,b23,c,*d3; void fun1(int *p); void fun

8、2(int (*p)3); void fun3(int *p); void fun4(int p3); void fun5(int p); void fun6(int p23); void fun7(int (&p)3);函数 不会产生编译时刻的可能值(但逻辑上不一定都对) 函数fun1fun2fun3fun4fun5fun6fun7 不会产生编译时刻的可能值(但逻辑上不一定都对) a, &ai, *b ,bi,&bij ,&c ,di b,b+i, d a, &ai, *b ,bi,&bij ,&c ,di a, &ai, *b

9、 ,bi,&bij ,&c ,di b a为什么可以有这样的搭配,原因如下:对于fun1 fun4 fun 5: 在编译器看来fun1,fun4,fun5的声明是一样,在编译时候,编译器把数组的大小舍去不考虑,只考虑它是一个指针,也就是说有没有大小说明是一样的,所以三者的形式都是fun1的形式(其实只要提供了int*指针就可以了);对于fun7 :以上的解释对于引用是不适用的,如果变量被声明为数组的引用,那么编译器就要考虑数组的大小了,那么必须和声明一模一样(所以fun7就只有a合适);对于fun2:p是一个指向一个含有3个元素的数组,这样b和b+i正好合适,而a却不是(它是指

10、向a0的,不是指向这个数组的);对于fun3:p是一个指针的指针,而d指向d0,同时d0又是一个指针,所以d就是一个指针的指针。但是b却不是(它是一个2*3的矩阵也就是年int 23类型);对于fun6,p是一个2*3的数组类型,和b恰好完全匹配;二、函数指针、函数的指针参数以及返回指针的函数A) 函数指针C+规定,一个函数的地址就是这个函数的名字。我们需要指出的就是一个指针需要指定类型是为了后来的指针解析时候使用,通过指针有效快速访问对象。那么对于函数的指针,它要表示出该函数的那些特性才能满足解析的唯一性呢?答案就是一个函数的特性有它的参数列表和返回类型。下面是一个函数指针的例子:int (

11、*p)(int I,int j);不能是int *p(int I,int j),这样就变成了返回指针的函数声明了。在C+中处于对安全性的考虑,指针和它指向的对象要类型一致,也就说上面的指针所指向的函数的特性要和它一模一样:例如指向int min(int I,int j);是可以的。但是指向int min(double I ,double j);是不可以。函数指针也和其他的指针一样,在使用的时候很怕发生"悬空",所以在使用的时候同样要判断有效性,或者在定义的时候就初始化。int (*p)(int I,int j)=min; int (*p)(int I,int j)=&

12、;min; int (*p)(int I,int j)=0;B) 函数的指针参数函数指针可以作函数的参数:例如我们有一个积分的算法,对于不同的数学函数可以进行积分(我们这里假设函数都是一元的);那么我们的算法接口可以定义为:templateT integrate( T lower, T upper , T (*)(T)=0 )throw(integrated_exp);这里的最后的参数是一个函数的指针,并且被设定缺省值为0。这个函数返回一个值,同时需要一个参数。假如加入我们有这样的一个函数:double line(double x) return a*x+b;那么我就可以使用了。函数指针还可以

13、作为返回类型(注意不是函数!,某个特定的函数是不可以作为返回类型的。)假设:typedef int (*PF)(int ); PF getProcessMethod( );/trueC) 返回指针的函数一个函数的返回是函数的重要接口之一,c+的一个重要的强大的功能就是能够设计足够复杂和好用的用户自定义类型。而同时处理和传递这些类型也是很麻烦的一件事情,我们不想把我们的时间都花在这些对于我们的实际工作没有很实质帮助的拷贝上,解决这个问题就要依赖我们的接口设计:c和c+都提供了相应的解决方案,在c+中我们可是使用引用,讲他们作为函数的实际参数,或者我们在函数的实际参数中使用一个指针等。同样我们还可

14、以使用一个函数返回一个指针:但是这是一个很不好解决的问题!我们首先容易出错的是:将一个局部变量的地址传出来!例如:UserType * Process( ) UserType ut(param-list); /process ut; return &ut;/ 这个变量在我们的函数结束的时候就被销毁了,尽管地址可以传出去,但是这个地址已经不存在了,已经不能使用的东西,在这个函数之外却不知道,难免要出错!同时我还会有一个比较麻烦的问题:使用new,又容易造成内存泄露UserType * Process ( ) UserTpye *put=new UserType(param-list );

15、 /process put; return put; 我们在函数内部使用了一个new,分配了一个空间,这样传出来也是可以!就是说不会发生上面的问题了。但是用户通常都会忘记在程序的外面在把这个借来的空间还回去!内存空间就这样泄露了!可能也是这些另人无奈的问题,所以很多程序员把函数的参数设定为指针或者引用,以此来代替这种向外传输吧!总之,使用这种返回指针的函数要小心!三、类成员的指针类成员和一般的外部变量相互比较,不同就是它所在的域不同,这个域很重要,它决定了该变量可以使用的范围。那么一个指针如果要指向类的成员函数或者成员变量,那么除了要表达它的返回类型、参数列表或者类型之外,那么还要说明它所指向的变量(或者函数)的域,为了说明该域我们要使用类域限定:class NJUPT static double money=20000000; int num; public: NJUPT():num(10); int get()return num; double getMoney()reuturn money; 我们定义成员的指针为int NJUPT: *p;/指向int型成员变量 int (NJUPt:*)p()/指向int f()型成员函数。为了使用这些指针,我们需要使用该类型的变量或者指针。NJUPT s,*ps;那么调用的方式

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论