`

指针专题

阅读更多


一、数组的指针、指针数组以及指向指针的指针

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

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

  b

fun7

a

为什么可以有这样的搭配,原因如下:

  • 对 于fun1 fun4 fun 5: 在编译器看来fun1,fun4,fun5的声明是一样,在编译时候,编译器把数组的大小舍去不考虑,只考虑它是一个指针,也就是说有没有大小说明是一样 的,所以三者的形式都是fun1的形式(其实只要提供了int*指针就可以了);
  • 对于fun7 :以上的解释对于引用是不适用的,如果变量被声明为数组的引用,那么编译器就要考虑数组的大小了,那么必须和声明一模一样(所以fun7就只有a合适);
  • 对于fun2:p是一个指向一个含有3个元素的数组,这样b和b+i正好合适,而a却不是(它是指向a[0]的,不是指向这个数组的);
  • 对于fun3:p是一个指针的指针,而d指向d[0],同时d[0]又是一个指针,所以d就是一个指针的指针。但是b却不是(它是一个2*3的矩阵也就是年int [2][3]类型);
  • 对于fun6,p是一个2*3的数组类型,和b恰好完全匹配;

二、函数指针、函数的指针参数以及返回指针的函数

A) 函数指针
   C++规定,一个函数的地址就是这个函数的名字。我们需要指出的就是一个指针需要指定类型是为了后来的指针解析时候使用,通过指针有效快速访问对象。那 么对于函数的指针,它要表示出该函数的那些特性才能满足解析的唯一性呢?答案就是一个函数的特性有它的参数列表和返回类型。

下面是一个函数指针的例子:

int (*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)=&min;
int (*p)(int I,int j)=0;

B) 函数的指针参数
  函数指针可以作函数的参数:例如我们有一个积分的算法,对于不同的数学函数可以进行积分(我们这里假设函数都是一元的);
那么我们的算法接口可以定义为:

template<class T>
T integrate( T lower, T upper , T (*)(T)=0 )throw(integrated_exp);

  这里的最后的参数是一个函数的指针,并且被设定缺省值为0。这个函数返回一个值,同时需要一个参数。假如加入我们有这样的一个函数:

double line(double x){ return a*x+b;}

那么我就可以使用了。

函数指针还可以作为返回类型(注意不是函数!!,某个特定的函数是不可以作为返回类型的。)假设:

typedef int (*PF)(int );
PF getProcessMethod( );//true

C) 返回指针的函数
   一个函数的返回是函数的重要接口之一,c++的一个重要的强大的功能就是能够设计足够复杂和好用的用户自定义类型。而同时处理和传递这些类型也是很麻烦 的一件事情,我们不想把我们的时间都花在这些对于我们的实际工作没有很实质帮助的拷贝上,解决这个问题就要依赖我们的接口设计:c和c++都提供了相应的 解决方案,在c++中我们可是使用引用,讲他们作为函数的实际参数,或者我们在函数的实际参数中使用一个指针等。同样我们还可以使用一个函数返回一个指 针:但是这是一个很不好解决的问题!
我们首先容易出错的是:将一个局部变量的地址传出来!例如:

UserType * Process( )
{
  UserType ut(param-list);
  //process ut;
  return &ut;//
}

  这个变量在我们的函数结束的时候就被销毁了,尽管地址可以传出去,但是这个地址已经不存在了,已经不能使用的东西,在这个函数之外却不知道,难免要出错!
同时我还会有一个比较麻烦的问题:使用new,又容易造成内存泄露

UserType * Process ( )
{
  UserTpye *put=new UserType(param-list );
  //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;

那么调用的方式为:

cout<<s.*p;
cout<<(s->*p)();

  这个看起来似乎很奇怪!但是只要你想到我们定义的指针被限定在了类域中了(我们在开始定义的使用使用了NJUPT:: ),这么使用也是很自然的。
  如果一个类还有一些静态成员变量和静态成员函数,那么我是否也想这样使用呢?
答案是不用,静态成员我们就可以象使用外部的普通成员一样定义一个指针或者函数指针来访问就可以了,究其原因主要是这个成员的类型性质决定的。

double *p=&NJUPT::money;
double (*p)()=&NJUPT::getMoney(): 
分享到:
评论

相关推荐

    指针专题学习

    ### 指针专题学习:理解与应用 #### 引言 指针是计算机编程中一个核心且强大的概念,尤其在C语言中占有极其重要的地位。掌握指针不仅能够提升编程效率,还能深入理解计算机内存的工作原理。本文将基于给定的资料,...

    c语言指针专题

    ### C语言指针专题 #### 一、数组的指针、指针数组以及指向指针的指针 在C语言中,指针是理解和高效编程的关键。本节将深入探讨几种与数组相关的指针概念,包括数组的指针、指针数组及指向指针的指针。 ##### A)...

    C++指针专题(Version 1.0)

    ### C++指针专题知识点详解 #### 一、引言 C++作为一种强大的面向对象的编程语言,不仅兼容C语言的基础语法,还在其基础上增加了许多高级特性。其中,指针作为C++语言的一个核心概念,对于理解和掌握C++至关重要。...

    No C++ 指针专题讲解

    "No C++ 指针专题讲解"是专为初学者设计的教程,旨在帮助他们克服理解指针的难点。这个教程可能包含了从基础到进阶的指针用法,包括指针的定义、声明、初始化,以及它们在内存管理和函数调用中的应用。 首先,我们...

    数组与指针专题

    ### 数组与指针专题详解 #### 一、一维数组与指针 在讨论一维数组与指针之前,我们需要明确几个基本概念。数组名实际上代表数组的起始地址,而通过这个地址,我们可以访问整个数组内的元素。在C语言中,数组的空间...

    C语言指针专题ppt.ppt

    C语言指针专题 本指南旨在向学员介绍指针的概念以及各种指针的用法,通过本章的学习,学员应该掌握如下知识: 一、内存、地址与指针的概念 * 变量的存储位置:内存 * 不同Scope的变量,存储在不同内容区域 * 变量...

    C++ 学习资料,帮助文档,指针专题

    本文将深入探讨C++的学习资料、帮助文档以及指针专题,旨在为初学者提供详尽的指导。 首先,"CLib.chm"很可能是一个包含C++标准库的离线帮助文件。C++标准库是C++编程的基础,它提供了各种容器(如vector、list)、...

    C++指针专题讲解.zip

    **C++指针专题讲解** 在C++编程语言中,指针是其核心特性之一,它扮演着数据存储地址的“引用者”角色,使得我们可以直接操作内存中的数据。本专题将深入探讨C++指针的各个方面,帮助你全面理解和掌握这一强大的...

    Day7-指针专题二案例代码

    Day7-指针专题二案例代码

    指针专题 指针大集合。。。轻松学会指针。。

    指针是C/C++编程语言中的核心概念,它在程序设计中扮演着至关重要的角色。指针能够存储内存地址,允许我们直接操作内存,从而实现高效的数据操作和复杂的程序结构。下面将深入探讨指针的基本概念、类型、运算、指针...

    最经典C++指针专题

    本专题将深入探讨指针的使用方法,旨在帮助理解其基本概念、操作和应用。 1. **指针的基本概念**: 指针是一个变量的地址,它是一个整数形式的常量。指针变量则是专门用于存储地址的变量,它可以指向整型、浮点型...

    C++指针专题

    C++指针专题

    C++的String类及其成员函数和智能指针专题.pdf

    需要注意的是,智能指针中存储的是指向资源的指针,因此它们本身通常占用的内存是固定的,不会随着管理的资源大小变化而变化,这使得它们在管理大型数据结构时特别有用,例如管理大型数组或动态分配的二维数组。...

    C++类中的各种成员,嵌套类,友元,this指针专题(pdf)

    本文将详细介绍C++类中的各种成员,包括类中的静态成员变量、成员函数、常量成员变量、常量成员函数、常量静态数据成员、对象成员、类成员指针、嵌套类、友元、this指针以及.*和-&gt;*运算符。 #### 二、类中的静态...

    黄邦勇帅C++专题讲解

    C++类中的各种成员,嵌套类,友元,this指针专题 C++继承,虚函数与多态性专题 C++宏,预处理器,RTTI,typeid与强制类型转换专题 C++函数专题 C++构造函数,复制构造函数和析构函数专题 C++的String类及其成员函数和智能...

    专题三 指针(教学PPT+练习题)

    **指针专题详解** 在计算机科学中,C++是一门强大的编程语言,它结合了面向过程和面向对象的编程范式。在C++中,指针是编程中的一个核心概念,它扮演着至关重要的角色。指针允许我们直接操作内存地址,这在处理数据...

    c和指针课后题答案

    - 指针专题涉及指针的高级话题,比如指针与结构体的使用、函数指针、指向指针的指针等。 - 高级指针话题还可能包括指针与内存管理相关的复杂问题,如内存泄漏和野指针。 8. 编译和运行环境 - C语言程序的编译和...

    无标题的笔记本.pdf

    "指针专题" 指针是一种复杂的数据类型,在编程语言中,它可以指向某个内存地址,存储该地址的值或变量。下面是关于指针的详细知识点: 一、指针的定义和概念 指针是一个变量,它存储了另一个变量的内存地址。指针...

Global site tag (gtag.js) - Google Analytics