观察和发现,* 为什么...
我决定做一些实验并得出结论,
观察 1- 如果链表不为空,则我们可以仅使用单个指针将节点添加到其中(显然是在末尾)。
int insert(struct LinkedList *root, int item){
struct LinkedList *temp = (struct LinkedList*)malloc(sizeof(struct LinkedList));
temp->data=item;
temp->next=NULL;
struct LinkedList *p = root;
while(p->next!=NULL){
p=p->next;
}
p->next=temp;
return 0;
}
int main(){
int m;
struct LinkedList *A=(struct LinkedList*)malloc(sizeof(struct LinkedList));
A->data=5;
A->next=NULL;
cout<<"enter the element to be inserted\n"; cin>>m;
insert(A,m);
return 0;
}
这很容易解释(基础知识)。我们在主函数中有一个指针,它指向列表的第一个节点(根节点)。在insert()
函数中,我们传递根节点的地址,并使用此地址到达列表的末尾并添加一个节点。因此,我们可以得出结论:如果我们在函数中(而不是主函数)具有变量的地址,则可以从该函数对该变量的值进行永久更改,这将反映在主函数中。
**观察2-当列表为空时,上述添加节点的方法失败了。
int insert(struct LinkedList *root, int item){
struct LinkedList *temp = (struct LinkedList*)malloc(sizeof(struct LinkedList));
temp->data=item;
temp->next=NULL;
struct LinkedList *p=root;
if(p==NULL){
p=temp;
}
else{
while(p->next!=NULL){
p=p->next;
}
p->next=temp;
}
return 0;
}
int main(){
int m;
struct LinkedList *A=NULL;
cout<<"enter the element to be inserted\n";
cin>>m;
insert(A,m);
return 0;
}
如果您不断添加元素并最终显示列表,则会发现列表没有发生任何更改,仍然为空。
我想到的问题是,在这种情况下,我们仍然传递了根节点的地址,为什么修改不会作为永久修改发生,并且主函数中的列表没有发生任何更改。为什么?为什么?为什么?
然后我观察到一件事情,当我写A=NULL
时,A
的地址变为0。这意味着现在A
没有指向内存中的任何位置。因此,我删除了A=NULL;
这行,并对插入函数进行了一些修改。
一些修改(下面的insert()
函数只能向空列表添加一个元素,只是为了测试目的编写此函数):
int insert(struct LinkedList *root, int item){
root= (struct LinkedList *)malloc(sizeof(struct LinkedList));
root->data=item;
root->next=NULL;
return 0;
}
int main(){
int m;
struct LinkedList *A;
cout<<"enter the element to be inserted\n";
cin>>m;
insert(A,m);
return 0;
}
上述方法也失败了,因为在
insert()
函数中,根节点存储的地址与
main()
函数中的
A
相同,但是在
root= (struct LinkedList *)malloc(sizeof(struct LinkedList));
这一行之后,
root
中存储的地址发生了变化。因此,现在
insert()
函数中的
root
和
main()
函数中的
A
存储不同的地址。
因此,正确的最终程序应该是:
int insert(struct LinkedList *root, int item){
root->data=item;
root->next=NULL;
return 0;
}
int main(){
int m;
struct LinkedList *A = (struct LinkedList *)malloc(sizeof(struct LinkedList));
cout<<"enter the element to be inserted\n";
cin>>m;
insert(A,m);
return 0;
}
但我们不想为插入编写两个不同的函数,一个是当列表为空时,另一个是当列表不为空时。现在双指针出现了,使事情变得容易。
我注意到的一件重要的事情是指针存储地址,当与 '*' 一起使用时,它们会给出该地址上的值,但指针本身也有自己的地址。
现在这里是完整的程序,稍后解释概念。
int insert(struct LinkedList **root,int item){
if(*root==NULL){
(*root)=(struct LinkedList *)malloc(sizeof(struct LinkedList));
(*root)->data=item;
(*root)->next=NULL;
}
else{
struct LinkedList *temp=(struct LinkedList *)malloc(sizeof(struct LinkedList));
temp->data=item;
temp->next=NULL;
struct LinkedList *p;
p=*root;
while(p->next!=NULL){
p=p->next;
}
p->next=temp;
}
return 0;
}
int main(){
int n,m;
struct LinkedList *A=NULL;
cout<<"enter the no of elements to be inserted\n";
cin>>n;
while(n--){
cin>>m;
insert(&A,m);
}
display(A);
return 0;
}
以下是观察结果:
1. root 存储指针 A 的地址(&A),*root 存储指针 A 所存储的地址,**root 存储指针 A 所指向的值。简单来说,root=&A,*root=A,**root=*A。
2. 如果我们写入 *root=1528,则表示存储在 root 中的地址的值变为 1528,由于存储在 root 中的地址是指针 A 的地址(&A),因此现在 A=1528(即存储在 A 中的地址为 1528),并且这种更改是永久性的。
每当我们更改 *root 的值时,实际上我们正在更改存储在 root 中的地址的值,并且由于 root=&A(指针 A 的地址),因此我们间接地更改了 A 的值或存储在 A 中的地址。
现在,如果A=NULL
(列表为空)*root=NULL
,因此我们创建第一个节点并将其地址存储在*root
中,即间接地将第一个节点的地址存储在A
中。如果列表不为空,则除了使用单个指针执行先前函数时更改了根为*root
之外,其他所有内容都相同,因为现在存储在根中的内容现在存储在*root
中。
malloc()
的结果进行强制转换。移除强制转换会使代码更易读,并符合惯用写法。 - Kerrek SB