二级指针
[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是传值的,一级指针如果不是在返回值中返回,你申请的内存空间在返回是被销毁,而用二级指针,它将是指向你申请的内存的首地址。另外,一定要记住指针是一个值,是内存的编号。