关于C语法实现链表的问题分析

本文深入解析链表结构体声明方式的差异,探讨结构体指针与引用的使用场景,以及函数参数传递机制对链表操作的影响,旨在帮助读者理解链表编程的核心概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题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()改变时,其仅改变的仅仅只是函数体内的临时变量,而非主函数中内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值