理解二级指针

来源:互联网 发布:android wifi定位源码 编辑:程序博客网 时间:2024/05/18 22:16

举两个小例子来看看吧。

1。用二级指针动态申请二维数组:

int main(){    int m , n , **p;    scanf("%d%d" , &m , &n);    p = (int **)malloc(m * sizeof(int *))    //C++中建议使用:p = new int* [m];    for(i = 0 ; i < m ; i++)        p[i] = (int *)malloc(n * sizeof(int));         //C++:p[i] = new int[n];}



这样就实现了二维数组的动态申请,因为一般数组声明时,不允许下标是变量,所以如果想动态决定数组各维的大小,最好这样做。

2。使用二级指针传递参数,可以在函数内部修改一级指针。
或许,你已经很熟悉通过传递一级指针,可以在函数内部修改实参指针指向的内容:如:
void f(char *p){    p[2] = 'a';//由实参指向的函数外部的数组的内容就被改变了。    ……}


但是,如果我们想改变实参本身呢?也就是说,我们连指针值都要改变,如果使用:
void f(char *p){    p = (char *)malloc(10 * sizeof(char))    //或C++中:p = new char[10];    ……}


就不行了,因为在函数内部不能通过改变形参的值来改变实参(注意这里实形参是指针p,上面的那个函数并没有改变形参,只是改变了形参指向的内容,只是由于形参和实参的值相同,也就是指向同一块内存,所以也就是改变了实参指向的内容,这个时候就要用二级指针了)。
void f(char **p){    *p = new char[10];    *p[2] = 'a';    ……}


可以这样说,传入一个N级指针,就可以修改N-1级指针,原因就是C的参数传递是值传递的,直接修改形参根本改变不了实参,但可以改变行参指针指向的内容,而N级指针指向的内容就是一个N-1级指针(你可以把非指针变量理解为0级指针),另外,你可能感觉这样使用似乎有问题,因为原先的内存空间没有释放,是的,这只是个为了说明问题的简化了的例子,实际的应用场合要比这合适的多。

最后,指针是C/C++语言的精华,但理解指针还是应该从指针本身去理解,二级指针只是指针的一种,不应该在这里钻牛角尖,多级指针其实还是指针,只不过它指向的内容内容仍是指针而已。



接下来通过二叉查找树的插入算法来分析:

算法大体的C语言描述:

Status SearchBST1(BiTree *T,KeyType key,BiTree f,BiTree *p)  { /* 在根指针T所指二叉排序树中递归地查找其关键字等于key的数据元素,若查找 */   /* 成功,则指针p指向该数据元素结点,并返回TRUE,否则指针p指向查找路径上 */   /* 访问的最后一个结点并返回FALSE,指针f指向T的双亲,其初始调用值为NULL */   if(!*T) /* 查找不成功 *///①   {     *p=f;     return FALSE;   }   else if EQ(key,(*T)->data.key) /*  查找成功 */   {     *p=*T;     return TRUE;   }   else if LT(key,(*T)->data.key)     return SearchBST1(&(*T)->lchild,key,*T,p); /* 在左子树中继续查找 */   else     return SearchBST1(&(*T)->rchild,key,*T,p); /*  在右子树中继续查找 */ }

Status InsertBST(BiTree *T, ElemType e) { /* 当二叉排序树T中不存在关键字等于e.key的数据元素时,插入e并返回TRUE,否则返回FALSE。 */   BiTree p,s;   if(!SearchBST1(T,e.key,NULL,&p)) /* 查找不成功 *///②   {     s=(BiTree)malloc(sizeof(BiTNode));     s->data=e;     s->lchild=s->rchild=NULL;     if(!p)       *T=s; /* 被插结点*s为新的根结点 *///③     else if LT(e.key,p->data.key)       p->lchild=s; /* 被插结点*s为左孩子 */     else       p->rchild=s; /* 被插结点*s为右孩子 */     return TRUE;   }   else     return FALSE; /* 树中已有关键字相同的结点,不再插入 */ }

对算法中二级指针的理解:

  1. 在这个函数中T也使用了二级指针,但是我认为这里二级指针没有什么用武之地,一级足够了吧。因为这里的T仅仅起到指示递归过程中的根节点的作用,而且T的改变对主调函数来说是没有意义的。
  2. 在这里传递给递归函数SearchBST1的是指针p的地址,在这里使用了二级指针。因为我们需要通过p来实现:若查找成功指向查找到的节点,若查找失败指向查找路径上最后一个节点,所以这其中p必然会不断移动,而且p是通过传参在其他函数中实现的移动。所以如果仅仅传递p,SearchBST1得到的仅仅是一份拷贝,它是通过一个在栈中新定义的一个指针变量来实现的移动,而且在SearchBST1函数结束后新定义的变量会收回(当然,因为二叉查找树的结构是存储在堆中的,最后可以通过返回局部变量的指针来实现对p的定位,这样就不需要二级指针了。但是因为要通过一个布尔值来判断查找的结果,所以还是不要用这种方法比较好),就无法实现下面通过p进行的插入操作了。
  3. 这里的T也是个二级指针,它的使用方法跟一开始讲的第2个小例子差不多。一般调用InsertBST的函数会定义一个变量BiTree T,然后通过InsertBST(&T,e)进行调用。因为要使T指向在被调函数中分配给它的内存的地址,所以最好使用二级指针。

0 0