我在网上看到的所有实现都使用指针声明节点,然后将使用malloc为它们创建空间,如下所示:
struct Node
{
int data;
struct Node *next;
};
int main()
{
struct Node* head = NULL;
struct Node* second = NULL;
struct Node* third = NULL;
head = (struct Node*)malloc(sizeof(struct Node));
second = (struct Node*)malloc(sizeof(struct Node));
third = (struct Node*)malloc(sizeof(struct Node));
...
但是我也可以这样创建没有指针和malloc的对象:
struct node {
int id;
struct node* next;
};
struct node head = {0, NULL};
struct node one = {1, NULL};
struct node two = {2, NULL};
struct node tail = {3, NULL};
int main(){
head.next = &one;
one.next = &two;
two.next = &tail;
...
我的问题是,为什么第一种方法大部分是使用的,为什么我们需要将每个节点声明为指针,为什么需要malloc?
(只需指出,我知道为什么将struct Node *next;
声明为结构声明中的指针)。
答案 0 :(得分:3)
您应该使用局部变量而不是全局变量来执行此操作,但是总体思路是相同的。您还应引导使用数组,而不要堆放其他不相关的变量:
struct node {
int id;
struct node* next;
};
int main(){
struct node nodes[4];
for (int i = 0; i < 4; ++i) {
nodes[i].id = (3 - i);
if (i != 0) {
nodes[i].next = &nodes[i-1];
}
}
return 0;
}
为了方便起见,类似的东西以相反的顺序将它们组装在一起,但是最初它们在内存方面都归为一类。 使用
malloc
是因为您通常不知道将要拥有多少,或者它们会意外地添加和删除。通用解决方案将根据需要分配它们。专门的实现可能会将它们分配为一个块,但这是高度情况。
这里nodes
的寿命仅在该函数内,因此该函数结束后,数据就会消失。换句话说:
struct node* allocateNodes() {
struct node nodes[10];
return &nodes; // Returns a pointer to an out-of-scope variable, undefined behaviour
}
那行不通。您需要更长寿命的分配,这正是malloc
提供的分配:
struct node* allocateNodes() {
struct node *nodes = calloc(10, sizeof(struct node));
return nodes; // Returns a pointer to an allocated structure, which is fine
}
问题是,如果您malloc
负责调用free
来释放内存,那么它将变得更多工作。
您将看到代码中使用的两种样式,这取决于所讨论变量的必需寿命。
答案 1 :(得分:3)
如果您提前知道列表中将有多少项,那么最好使用数组而不是列表。链表的全部要点是能够在运行时增长到未知大小,并且这需要动态内存分配(即malloc)。