二级指针

[cpp] view plaincopy
1   void remove_if(node ** head, remove_fn rm)    
2   {    
3       for (node** curr = head; *curr; )    
4       {    
5           node * entry = *curr;    
6           if (rm(entry))    
7           {    
8               *curr = entry->next;     
9               free(entry);    
10           }    
11           else    
12               curr = &entry->next;     
13       }    
14   }   
上面的例子就是文章给出的一个关于二级指针的例子,文章给出的评论是,最关键的部分在于:链表中的链接都是指针,因此指针到指针是修改链表的首选方案。

     我对作者的用法起初是不大理解,我想不大理解的根源是我对程序对堆栈的用法还不够深入了解。首先,采用一级指针最好是方便修改指针对象的值,采用二级指针最好是方便修改指针的值。理解了这两点,对上面的用法也就可以理解了,函数的用意是删除指定的链表节点,传统的用法是采用一级指针,传入链表头节点,然后判断删除的是头节点,还是其他节点,然后依次寻找一下一个节点。但是linus认为采用这种用法的人不懂指针,懂指针的人采用上面的做法。

    分析一下上面的代码,有两行代码,第8行和第12行,这两行的用法确实高明,第8行改变指针的指向为下一个节点,不论是头节点还是其他节点都适用,第12行改变指针本身的指向为下一个节点,这个好处是不改变调用函数指针的指向。


如果用create()那必须用NODE **,因为函数要对NODE* 操作,如果参数只传
NODE*, 那只是对NODE* 做了一份拷贝,当返回时函数里的NODE*被摧毁,你函数体
外也得不到什么。
例如:
  void swap(int a, int b)  
 {
     int temp = a;
     a = b;
     b = temp;
  }

  void swap1(int *a, int *b)
  {
    int temp = *a;
    *a = *b;
    *b = temp;
  }

  void main()
 {
   int m = 1, n = 2;
   swap(m, n);
   cout << m << endl;  //1
   cout << n << endl;  //2 没有变化

   swap1(&m, &n);
   cout << m << endl;  //2
   cout << n << endl;  //1 有变化
 }
 当然你不喜欢传二级指针,那就传引用 Create(NODE *&p);


NODE **hpt 一般用在要改变实参指针所指向的内存时!
也可用 NODE*& rpt 代替!
是说我传递一个指针要指向函数中新开辟的内存单元,
在函数返回时我要保留这个新的地址!
这是 NODE *hpt 所做不到的!
所以楼主可能没有看错!!!

例如:

#include<iostream>
typedef int NODE;
using namespace std;

void fun(NODE **pht)
{
*pht = new NODE(10);
}

void main(void)
{
NODE value = 5;
NODE* p = &value;
cout << *p << endl;
fun(&p);
cout << *p << endl; //访问p指向的新内存单元
delete p;
}

//如果用*pht则不能实现!而且在函数fun中还有内存泄露!!!

#include<iostream>
typedef int NODE;
using namespace std;

void fun(NODE *pht)
{
pht = new NODE(10);
}

void main(void)
{
NODE value = 5;
NODE* p = &value;
cout << *p << endl;
fun(p);
cout << *p << endl;
}


总结:指针也是传值传递,当我们要在被掉函数里面改变调用函数一级指针的值时,就需要以二级指针作为参数。这种情况是经常碰到的,比如在链表(无头结点)操作时是通过链表第一个元素来找到其他所有链表中的元素,如果删除操作时删除的正好是第一个元素,那么这时就要改变链表头指针的指向了。当然还有在二叉树操作时当删除的刚好是树根结点,此时也要改变一级指针的指向。

 

2、有关指针的数据类型小结(来着《c语言程序设计》谭浩强)



typedef struct _node
{
void *data;
struct _node *prior;
struct _node *next; 
}Node,*PNode;
用来定义指针结构体类型:如PNode p效果同struct _node* p


void getmem(char* ptr)
{
    ptr = (char*)malloc(sizeof(char) * 10 );
}
这个函数试图在堆上申请一个长度为10的内存空间并返回给ptr。然而编译器只是把ptr的值复制到一个临时变量_ptr中,然后这个内存空间的首地址赋给_ptr,在调用返回之后,_ptr被清0,而ptr并没有得到这个内存区。
而:
void getmem(char** ptr)
{
    *ptr = (char*)malloc(sizeof(char)*10);
}

char* getmem(char* ptr)
{
    ptr = (char*)malloc(sizeof(char*) * 10);
    return ptr;
}
是等价的。

你要是不想用二级指针,可以用返回指针来得到——我只在Linux和UNIX环境下做过。注意c是传值的,一级指针如果不是在返回值中返回,你申请的内存空间在返回是被销毁,而用二级指针,它将是指向你申请的内存的首地址。另外,一定要记住指针是一个值,是内存的编号。


posted on 2014-09-22 20:20  迪迪520  阅读(285)  评论(0编辑  收藏  举报

导航