我正在学习C,在这个程序中,我正在尝试实现一个简单的链表。
列表的每个节点都包含一个整数,以及指向下一个节点的指针。
指针head
指向列表中的第一个节点,但最初列表为空,因此我初始化了head = NULL
。
我想在列表上执行两项操作 - 填充它并打印它。
要填充列表,我正在使用两个参数调用函数insert_node
:head
和我要插入的整数。
问题是我需要函数insert_node
来更改head
的值(因此它指向更新的列表,而不是NULL)。我不知道该怎么做,所以我把head
作为一个全局变量,我试图改变它的价值。
出于某种原因,即使在函数head
内更改insert_node
的值,当我再次调用该函数时,head仍然具有NULL值。
问题:
为什么全局变量值不会全局更改?
我知道使用全局变量不是一个好习惯,那么如何正确更新指向列表的指针呢?
我在考虑让insert_node
函数实际返回指向列表的指针,这是一个好方法吗?
#include<stdio.h> #include<stdlib.h> struct node { int data; struct node *link; }; void insert_node(struct node *head, int n); void print_list(struct node *head); struct node *head = NULL; main() { int i; for(i=1; i<5; i++) insert_node(head, i*i); print_list(head); } void print_list(struct node *head) { if(head == NULL) return; else { printf("%i ", head->data); print_list(head->link); } return; } void insert_node(struct node *head, int n) { struct node N = {n, NULL}; struct node *next, *prev; int prev_data = 0; //case one: list is empty - point head to N, and set N.link to NULL if(head == NULL) head = &N; //case two: n is less than first element in the list: else if(n < head->data) { N.link = head; head = &N; } else { next = head; //case three: N.data is equal to existing element, do nothing: while(next != NULL) { if(n == next->data) { printf("this element already exists.\n\n"); return; } prev = next; //save the current element next = next->link; //look at the next element } //case four: N.data is greater than last element: if(n > prev->data) { prev->link = &N; return; } //case five: N.data is in between list elements: next = head; while(next != NULL) { prev_data = next->data; //save the current element prev = next; //save pointer to current element next = next->link; //look at the next element if((n > prev_data) && (n < next->data)) { prev->link = &N; N.link = next; return; } } } return; }
答案 0 :(得分:5)
head
按价值传递给功能insert_node()
。insert_node()
生成局部变量(顺便说一句,它也有名称head
,这可能会让你感到困惑,因为它是本地的而不是全局的)。修改了本地head
,并且这些更改在全局变量head
中不可见。它就是所谓的阴影(具有相同名称但在本地范围内的变量与具有相同名称的任何其他变量不同)。<强>声明强>
void insert_node(struct node **ptr_to_head, int n);
<强>用法强>
insert_node(&head, 5);
现在,您可以通过在ptr_to_head
函数中解除引用insert_node
来修改头部:
(*ptr_to_head)=&new_node;
^ ^
| |
head = value returned by malloc
是的,您可以从insert_node函数返回head
,但不要忘记在main函数中对head
进行赋值。
答案 1 :(得分:1)
您添加了一个名为head的全局变量,但是您忘记删除具有相同名称的函数insert_node,print_list等上的参数。本地优先于全局,因此您的分配将分配给本地而非全局。
删除具有相同名称的参数,问题就会消失。
我不是在考虑使用全局变量:)
答案 2 :(得分:1)
在评论中,我说插入代码不需要像你一样多的情况。这是证明。它包括释放分配列表的代码。请注意,特殊情况较少(只有三个:重复输入,插入头部,插入其他地方)。
#include <stdio.h>
#include <stdlib.h>
struct node
{
int data;
struct node *link;
};
void insert_node(struct node **head, int n);
void print_list(struct node *head);
void free_list(struct node **phead);
void test_insert(struct node **head, int n);
int main(void)
{
struct node *head = NULL;
free_list(&head);
for (int i = 1; i < 5; i++)
test_insert(&head, i*i);
test_insert(&head, 0);
test_insert(&head, 7);
for (int i = 1; i < 6; i++)
test_insert(&head, i*i - 3);
test_insert(&head, 7);
test_insert(&head, 0);
free_list(&head);
return 0;
}
void insert_node(struct node **phead, int n)
{
struct node *node = malloc(sizeof(*node));
if (node == NULL)
{
fprintf(stderr, "Failed to allocate node for %d\n", n);
exit(1);
}
node->data = n;
struct node *head = *phead;
struct node *next = head;
struct node *prev = NULL;
while (next != NULL && n > next->data)
{
prev = next;
next = next->link;
}
if (next != NULL && n == next->data)
free(node);
else
{
node->link = next;
if (prev == NULL)
*phead = node;
else
prev->link = node;
}
}
void test_insert(struct node **head, int n)
{
printf("%2d:", n);
insert_node(head, n);
print_list(*head);
}
void print_list(struct node *head)
{
while (head != NULL)
{
printf(" %2i", head->data);
head = head->link;
}
putchar('\n');
}
void free_list(struct node **phead)
{
struct node *head = *phead;
while (head != NULL)
{
struct node *next = head->link;
free(head);
head = next;
}
*phead = 0;
}
示例输出:
冒号左边的值是'inserted';右边的值是结果列表。
1: 1
4: 1 4
9: 1 4 9
16: 1 4 9 16
0: 0 1 4 9 16
7: 0 1 4 7 9 16
-2: -2 0 1 4 7 9 16
1: -2 0 1 4 7 9 16
6: -2 0 1 4 6 7 9 16
13: -2 0 1 4 6 7 9 13 16
22: -2 0 1 4 6 7 9 13 16 22
7: -2 0 1 4 6 7 9 13 16 22
0: -2 0 1 4 6 7 9 13 16 22