【案例1】【错误案例】
#include<iostream>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef struct charlink
{
char ch;
struct charlink *next;
} CharLink;
void insertCharLink(CharLink *q,const char *chr)
{
printf("#3 void insertCharLink q %p\n",q);
CharLink *t = (CharLink*)malloc(sizeof(CharLink));
t->ch = *chr;
t->next = NULL;
q->next = t;
q = q->next;
printf("#4 void insertCharLink q %p\n",q);
}
int main()
{
const char str1[] = {'2','3','5','\0'};
CharLink *p = (CharLink*)malloc(sizeof(CharLink));
p->next=0;
p->ch = 'Q';
CharLink *h = p;
int i=0;
printf("#1 main--initial p %p\n",p);
printf("#1 main--initial h %p\n",h);
cout<< "#1 p->ch " << p->ch<<endl;
while(str1[i])
{
printf("#2--%d main--while p %p\n",i+1,p);
printf("#2--%d main--while h %p\n",i+1,h);
printf("#2--%d main--while p->ch %c\n",i+1,p->ch);
printf("#2--%d main--while h->ch %c\n",i+1,h->ch);
if(h->next)
printf("#2 h->next->ch %c\n",h->next->ch);
insertCharLink(p,&str1[i]);
++i;
}
printf("# h->ch %c\n",h->ch);
printf("# h->next->ch %c\n",h->next->ch);
printf("#5 p %p\n",p);
printf("#5 h %p\n",h);
while(h->next)
{
cout<<h->next->ch<<endl;
h = h->next;
}
return 0;
}
【分析】main函数中的第一个while中的每一轮迭代,p->ch和h->ch都输出字符'Q',这表明头指针没有改变,而h->next->ch每轮都输出不同的值,但是按照尾部插入法的逻辑h->next->ch每轮输出的值都是字符'2',这个逻辑在子函数insertCharLink中确实有实现,但是却没有体现在main函数中。实际上在while循环中每次给到子函数insertCharLink的参数都是新传入的头指针,子函数insertCharLink中的语句"q = q->next;"虽然对传入的头指针做了修改,但子函数insertCharLink修改的头指针只不过是以值传递方式传递过来的址,子函数修改以值传递方式传递过来的址不会改变其在main函数中的址,所以即便子函数insertCharLink修改了头指针,main函数中的头指针还是原来的样子。所以每轮while循环读取的不是子函数insertCharLink修改后的头指针,而是原来的头指针。因此当你在main函数用h->next遍历(比如第二个while循环)charlink时,你会发现并未形成链,最终仅能遍历到的是头指针以及最后一轮插入尾部的指针。
【案例2】【正确案例】
#include<iostream>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef struct charlink
{
char ch;
struct charlink *next;
}CharLink;
void createCharLink(CharLink *p, const char *str1)
{
p->next=0;
printf("#2--1 createCharLink %p\n",p);
printf("#2--2 createCharLink p->ch %c\n",p->ch);
while(*str1)
{
CharLink *t = (CharLink*)malloc(sizeof(CharLink));
t->ch = *str1;
t->next = NULL;
p->next = t;
p=p->next;
++str1;
printf("#2--3 createCharLink %p\n",p);
printf("#2--4 createCharLink p->ch %c\n",p->ch);
}
}
int main()
{
const char *str1 = "235";
CharLink *p = (CharLink*)malloc(sizeof(CharLink));
p->ch = 'Q';
printf("#1 initial %p\n",p);
printf("#1 p->ch %c\n",p->ch);
createCharLink(p,str1);
while(p->next)
{
printf("#3 main--while %p\n",p);
printf("#3 p->next->ch %c\n",p->next->ch);
//cout << p->next->ch << endl;
p = p->next;
}
system("pause");
return 0;
}
【分析】传递给子函数createCharLink的指针是以值传递方式传递过来的址,在子函数createCharLink内部有对p指针(头指针)修改操作(例如,p=p->next;),但main函数中的头指针并没有改变。main函数中的头指针没有改变的原因是修改以值传递方式传递过来的址只能修改指针副本,而不能修改指针本身。而对p->next(头指针的next指针)的写操作修改的是p指针指向的内容。我们知道以值传递的方式接收传递过来的址,虽然更改指针本身的址不能在main函数中生效,但更改指针指向的内容会在main函数中生效,所以对p->next(头指针的next指针)的写操作可以修改p指针指向的内容(p->next等同于(*p).next)。所以采用以值传递方式传递头指针可以实现链表的创建(前提条件是头指针在传递之前必须有定义)。
那么问题来了,采用以值传递方式传递头指针可以实现链表的创建(前提条件是头指针在传递之前必须有定义),为什么【案例1】也是采用以值传递方式传递头指针,但却没有实现链表的创建呢?
因为,【案例1】的逻辑是在main函数的while循环中调用子函数insertCharLink,以尾部插入的方式创建链表,具体地来讲是用每一个新的元素创建一个新的节点,并且把新节点链接到上一个指针的next指针上。【案例1】的期望是随着新节点的加入,头指针不断向后移动,每当在循环中遍历头指针时,也就是访问到了需要链接到的上一个指针。然而不幸地是,在main函数while循环中遍历的头指针是没有向后移动的头指针,虽然在子函数insertCharLink中有向后移动头指针的操作,但头指针在子函数insertCharLink中向后移动之后,子函数insertCharLink头指针本身的值(也就是address)改变了;但这种改变仅发生在子函数内部,头指针变量本身的值的变化没有影响传入给子函数insertCharLink的那个头指针,因为main函数中传递给子函数的指针变量是值(数值的”值”)的形式传递的,读写该指针变量不会改变其自身的值(或者说不会改变指针的值,也就是说p的值),只能改变指针指向对象的值(也就是说*p的值)。如果想在子函数insertCharLink中改变指针变量自身的值,应该以址(地址的”址“)的方式传递指针或者以引用的方式向子函数insertCharLink传递指针。
[案例3] 修正案例1,采用址传递的方式传递
#include<iostream>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef struct charlink
{
char ch;
struct charlink *next;
} CharLink;
void insertCharLink(CharLink **q,const char *chr) //修改之处1
{
CharLink *t = (CharLink*)malloc(sizeof(CharLink));
t->ch = *chr;
t->next = NULL;
(*q)->next = t; //修改之处2
(*q) = (*q)->next; //修改之处3
}
int main()
{
const char str1[] = {'2','3','5','\0'};
CharLink *p = (CharLink*)malloc(sizeof(CharLink));
p->next=0;
CharLink *h = p;
int i=0;
while(str1[i])
{
insertCharLink(&p,&str1[i]); //修改之处4
++i;
}
while(h->next)
{
cout<<h->next->ch<<endl;
h = h->next;
}
system("pause");
return 0;
}
【案例4】修正案例1,采用引用传递的方式传递
#include<iostream>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef struct charlink
{
char ch;
struct charlink *next;
} CharLink;
void insertCharLink(CharLink *&q,const char *chr) //修改之处1
{
CharLink *t = (CharLink*)malloc(sizeof(CharLink));
t->ch = *chr;
t->next = NULL;
q->next = t;
q = q->next;
}
int main()
{
const char str1[] = {'2','3','5','\0'};
CharLink *p = (CharLink*)malloc(sizeof(CharLink));
p->next=0;
CharLink *h = p;
int i=0;
while(str1[i])
{
insertCharLink(p,&str1[i]);
++i;
}
while(h->next)
{
cout<<h->next->ch<<endl;
h = h->next;
}
system("pause");
return 0;
}