您的位置:首页 > 其它

链表-单向链表-值传递&地址传递

2015-12-20 23:13 351 查看
1. 单向链表算法实现

typedef struct _Node{
int number;
char name[12];
int age;
} Node;    // this is just a example

typedef struct _Link{
Node node;
struct _Link * Next;
} Link;

//add a node( current may be a head pointer
template<typename type>
void AddLinkNode(type * current, type * newNode);

//delete a node by idnumber
template<typename type, typename typeT>
void DeleteLinkNode(type* & head, typeT idNumber);

//iterator the link
template<typename type>
void Listview(type * head);

//add a node( current may be a head pointer
template<typename type>
void AddLinkNode(type * current, type * newNode)
{
if (current == NULL)  // the current is the head pointer
current = newNode;
else    // non head pointer
current->Next = newNode;
}

//iterator the link
template<typename type>
void Listview(type * head)
{
type * p;
for (p=head; p!=NULL; p = p->Next)
{
/*printf("%d %s %d", p->node.number, p->node.name, p->node.age);*/
printf("%d ", p->node.number);
}
}

template<typename type, typename typeT>
void DeleteLinkNode(type* & head, typeT idNumber)
{
type * p = head;
while (p!=NULL)
{
if (p->node.number == idNumber)
{
if (p==head) //the head pointer
{
head = p->Next;
//printf("clean node----%d %s %d", head->node.number, head->node.name, head->node.age);
free(p);  //clean the heap resource
p = head; //make the p pointer to the next
}
else // non head pointer
{
//find the pre-pointer of p
type * temp = head;
while (temp->Next != p)
temp =temp->Next;
temp->Next = p->Next;
free(p);  //clean the heap resource
p = temp->Next; //make the p pointer to the next
}
//printf("delete node ---- %d ", p->node.number);
//free(p);
//break;
}
else
p = p->Next; //put the pointer to the next
}
}
2. 错误代码示例及分析

-----------------------------------------------this is the wrong code---------------------------------
//delete a node by idnumber
void DeleteLinkNode(Link * head, int idNumber)   // wrong here
{
Link * p;
for (p = head;  p!=NULL; p = p->Next)
{
if (p->node.number == idNumber)
{
if (p==head) //the head pointer
{
head = p->Next;
}
else // non head pointer
{
//find the pre-pointer of p
Link * temp = head;
while (temp->Next != p)
temp =temp->Next;
temp->Next = p->Next;
}
//printf("delete node ---- %d ", p->node.number);
free(p);
break;
}
}
}
-------------------------------------------this is the wrong code-------------------------------------------


解释:
void DeleteLinkNode(Link
* head, int idNumber) -----------------(1)



template<typename type, typename typeT>

void DeleteLinkNode(type* & head, typeT idNumber) ------------------(2)

现象描述:

当使用(1)时,虽然在函数中改变了头指针,但是,在调用函数中head指针并未发生变化。

原因:

在(1)中,函数声明为一个指向link的指针,此时传入的是头指针head,这个head本身是一个指针。因此,在传输的过程中,head实际是按值拷贝,拷贝的是地址。因此,被调用函数中,head仅仅是调用函数中head的拷贝。

在(2)中,head是type*的一个引用,因此,head实际就是调用函数中的实际值,这实际上是引用拷贝。

通常来讲,出现这种错误的几率很小,因为void
DeleteLinkNode(Link * head, int
idNumber) 在使用过程中常常如下:

Link
link;

DeleteLinkNode(&link,19);

此时,传入的是link的地址。在调用函数中,能够修改link

但本题中,

Link
* head;

DeleteLinkNode(head,19);

此时,head是作为一个link * 的实参传入的,并不是一个link * 指针的地址或者引用。因此,会发生以上错误。

3. 总结

在处理这种问题时,应把握两点:
1)值拷贝和地址拷贝
值拷贝不能修改原来的值。如果传入的只是某种变量类型,如Link, Link *(注意传入指针变量),并不是对这些变量的取地址,那么仍旧是值拷贝;
地址拷贝能改变原来的值,但是地址拷贝要求传入某种变量的地址或者引用。变量可以分为两种,一中是常用变量,一种是指针变量,。对于常用变量,如int ,传入的是( int *)
或者& (int);如果是指针变量,想要改变指针的值,则必须传入该指针变量的地址或者引用,如(int* *)或者 &(int
*)。
其中,千万不要把传入的指针变量看作是可以修改的。如果形参中,没有对指针变量进行取地址操作,那么传入的指针变量仅仅是一个值拷贝。
2)MSDN中的数据结构定义总是定义一个变量和指针变量?
因为,当传入的变量是一个指针变量时,实际仅仅是对指针变量的值拷贝,并不能达到修改指针变量值得效果。而程序猿有时会混淆这种指针变量的传递,错误的以为传入的就是指针变量的地址,实际上传递的仅仅是变量,而不是地址。因此,MSDN在定义数据结构时,便定义一个数据结构的指针,这样就为程序猿的操作提供了便利。



便利之处在于:

1)定义指针变量如同定义一个常用变量一样简单

ex:

LIST_ENTRY * pentry;
PLIST_ENTRY pentry;

两者是等价的。
2)在形参中可以用* 或者& 来明确的指出传递的是地址还是变量的拷贝
地址传递:传递的可以是变量的地址,也可以是保存指针变量的地址,而且函数中对这些变量的修改会影响到原来的
传指针:
void foo(LIST_ENTRY * pentry); //传递的是一个变量的地址
void foo(PLIST_ENTRY *
pentry); //传递的是一个指针变量的地址,可以对指针的值进行修改
传引用:

void foo(LIST_ENTRY & pentry); //传递的是一个变量的引用

void foo(PLIST_ENTRY
& pentry); //传递的是一个指针变量的引用,可以对指针的值进行修改

--------------------------------------------------------------------------

值传递:传递的可以是变量,也可以是指针,但是函数中对这些变量的修改不会影响到原来的
传变量:
void foo(LIST_ENTRY
pentry); //传递一个变量,但函数中知识对改变了的拷贝

void foo(PLIST_ENTRY pentry);
//传递一个指针变量,但函数中只是对该变量的拷贝

从上述可以看出,无需再显示声明指针,只需直接调用指针变量的结构声明。而,*和&则表明可以修改这些传入的参数,不加&和*则表明仅仅是变量,无法修改他们自身。这样,利用&和*便清晰明白的将值传递和地址传递解释清楚了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: