问题1:结构体的申明
问题描述:
如下代码所示,两种结构体的区别以及含义?
struct Node{
int data;
Node *next;
};
typedef struct Node{
int data;
Node *next;
}*List;
问题分析:
关于typedef关键字的用法,可以百度一下。这里仅讨论为什么偏向于使用第二种结构体申明方式。
首先,*List表示一个Node类型的指针。在链表的实现过程中,我们都是对节点内容的修改。如果使用的是Node类型变量,而非指针,则我们的修改仅仅只在函数体内有效。可以看下面例子。
#include<bits/stdc++.h>
using namespace std;
void change(int a){
a = 100;
}
int main(){
int a = 10;
change(a);
cout << a;
return 0;
}
主函数输出的仍是a = 10。这是因为函数内的变量只是临时变量,调用过程中编译器也会为它创建堆栈,并对其进行保存(保存在临时堆栈中)。但是,这个堆栈会随着函数的结束而被释放,因此函数内的值仅仅只是引用一下,而非真正的修改。如何对其修改?
我们可以通过指针获取变量在内存的地址,通过对内存的修改,而达到目的。这也是为什么第二种结构体要申明为*List的原因。当然,我们也可以采用第一种结构体申明方式,但是在参数的传递过程中稍微麻烦一些。具体实现可以参照单链表篇博客。
问题2:关于函数参数传递
问题描述:
如下代码所示,为什么主函数传入的参数是List类型变量,而函数申明的参数却是(List &hed)?以及为什么要开辟内存空间?
void init_list(List &hed){
hed = new Node;
hed->next = NULL;
}
问题分析:
首先我们先解决第二个问题,为什么要分配内存空间?如上述问题所述,在链表的实现过程中,我们想的是对节点内容进行修改,但是主函数中申明的仅仅是一个Node类型的指针变量,它并不是一个节点。可以看以下例子。
int main(){
List hed;
Node node;
hed = &node;
cout << &hed << endl;
cout << &node << endl;
return 0;
}
通过打印结果发现,二者地址并不相同。因为指针也是一个数据类型,它也会有自己的内存地址。我们通过init_list(List &hed)函数传递过去的只不过是一个Node 类型的指针变量,我们希望的是通过这样的一个指针可以保存这个节点的内存地址,然后对其修改。这也是为什么要分配内存空间的原因。
接下来我们再来看第一个问题,本问题同问题1类似。参数:List &hed,&关键字表示引用,可以理解为变量的别名,实际上指向的是同一块内存空间。指针指向的是一个内存空间,&也指向一个内存空间,二者有什么区别呢?观察下面例子。
#include<bits/stdc++.h>
using namespace std;
typedef struct Node{
int data;
Node *next;
}*List;
void init_list(List &hed){
cout << hed << " " << sizeof(hed) << endl;
}
void init_test(List hed){
cout << hed << " " << sizeof(hed) << endl;
}
int main(){
List hed;
init_list(hed);
init_test(hed);
return 0;
}
上述例子打印结果相同。通过上例,还看不出二者区别。通过下例,我们再来比较。
void init_list(List &hed){
hed = new Node;
cout << hed << " " << sizeof(hed) << endl;
}
void init_test(List hed){
cout << hed << " " << sizeof(hed) << endl;
}
int main(){
List hed;
init_list(hed);
init_test(hed);//后
return 0;
}
二者输出结果,仍相同。此时的调用顺序是init_list先,init_test后。此时结果相同的原因是,init_list()指向了一个节点,而test内的指针同样指向这个节点,所以相同。但是我们调换下两个函数的顺序可以发现,打印的结果不同了。如下所示。
int main(){
List hed;
init_test(hed);//先
init_list(hed);
return 0;
}
再来看一个例子:
void init_list(List &hed){
cout << hed << " " << sizeof(hed) << endl;
}
void init_test(List hed){
hed = new Node;
cout << hed << " " << sizeof(hed) << endl;
}
int main(){
List hed;
init_test(hed);
init_list(hed);
return 0;
}
这次无论如何改变主函数中的调用次序,结果都是不相同的。这是什么原因呢?其实同问题1一样,都是临时变量的问题。首先,分析init_list()函数,init_list()传入的是引用类型参数,其只不过是主函数中hed指针的别名,因此当对他进行修改时,即相当于在主函数中对hed进行修改。如下例所示。
void init_list(List &hed){
cout << hed << " " << sizeof(hed) << endl;
}
void init_test(List hed){
cout << hed << " " << sizeof(hed) << endl;
}
int main(){
List hed;
hed = new Node;
init_test(hed);
init_list(hed);
return 0;
}
二者打印结果相同,无论次序改变与否。
而当通过init_test()改变时,其仅改变的仅仅只是函数体内的临时变量,而非主函数中内容。