C链接头总是NULL



我想在c中实现一个简单的链表,但是我只能在本地访问list元素,在list_append函数中,而不是在main函数中。

#include <stdio.h>
#include <stdlib.h>
typedef struct list_element {
struct list_element *next;
int val;
} list_element_t;
typedef struct list {
struct list_element *first;
} list_t;

int list_append(list_t *list, int value) {
if (list->first == NULL) { // if case 1
// This scope is accessed after each list_append-call. I'm expecting it to be accessed only once.
list = (list_t *) malloc(sizeof(list_t));
list_element_t *new_ele = (list_element_t *) malloc(sizeof(list_element_t));
new_ele->next = NULL;
new_ele->val = value;
list->first = new_ele;
printf("list firstn");
printf("%dn", list->first->val);
return value;
} else {
printf("Do nothing.n");
}

return -1;
}
int main (int argc, char* argv[]) {
list_t list;
list.first = NULL;
printf("insert 47: %dn", list_append(&list, 47));
printf("insert 11: %dn", list_append(&list, 11));
printf("%dn", list.first->val); // This will cause a seg fault.
}

我期望if case 1只在第一次list_append调用中被访问,但它也在第二次调用(以及任何进一步的调用)中被访问。

我将从稍微不同的角度来解决这个问题。让我们逐一查看这些行:

int list_append(list_t *list, int value) {
到目前为止,我们知道list是一个指针。它可能指向某个东西,也可能是NULL。很有可能,它至少有时指向某些东西,否则将它传递给函数就没有多大意义了!对象是一个列表。
if (list->first == NULL)

现在可以说list肯定指向某物,否则list->first就没有多大意义了。只能对指向对象的指针使用->

list = malloc(sizeof(list_t));

这一行使list忘记了它指向的是什么,而使它指向一个全新的东西(这里是malloc刚刚返回的list_t对象)。

当一个指针可以指向一个新的对象时,这是很棒的。但是旧的物体呢?它去了哪里?还有别的指针指向它吗?如果是这样,我们将如何处理这两个列表呢?如果没有,则存在内存泄漏。也许不再需要它了?如果是,应该在指向它的最后一个指针停止之前释放。在对象内部的指针所指向的对象也应该以同样的方式考虑。那些对象还需要吗?还有其他指针指向它们吗?等等。

这些问题真的很难回答。你不应该编写会引发难题的代码

与正确的代码比较:

int list_append(list_t *list, int value) {
if (list->first == NULL) { 
list->first = malloc(sizeof(list_element_t));

应该担心list->first指向的对象吗?不,没有这样的对象。这个指针是NULL!因此,如果您已经检查了NULL指针,则可以自由地对该指针进行赋值。

另一种不需要担心旧对象的情况是在局部声明指针时。

list_element_t* element = other_element;

这里也没有element在初始化之前指向的旧对象。

每次给指针赋值时,你都应该停下来问自己这些问题。在赋值之前,这个指针是否指向一个对象?如果是这样,这个物体去了哪里?其他指针会照顾它吗,还是它永远丢失了?

在某些情况下,您确实需要回答这些问题,例如,在遍历链表时:

while (current != NULL) { 
// do something
current = current->next;

这里current明确指向一个对象。这个对象会丢失吗?有没有别的东西指向那个物体?是的!它是列表中前一个节点的指针(或者列表本身,如果current是一个头)。这个包含所有节点的列表不会移动到任何地方。所以这个对象不会丢失,所以赋值current是安全的。

当然,如果你指定一个函数参数,你应该知道新价值函数里面只看到。但这是一个完全不同的故事。

你的list_append函数并不像你想象的那样。

int list_append(list_t *list, int value) {
if (list->first == NULL) { // if case 1
// This scope is accessed after each list_append-call. I'm expecting it to be accessed only once.
list = (list_t *) malloc(sizeof(list_t));
list_element_t *new_ele = (list_element_t *) malloc(sizeof(list_element_t));
new_ele->next = NULL;
new_ele->val = value;
list->first = new_ele;
printf("list firstn");
printf("%dn", list->first->val);
return value;
}

list是函数的本地参数。您已经修改了它,但是您没有修改它指向的列表。当函数退出时,该列表保持不变。

你可能想这样做:

int list_append(list_t *list, int value) {
if (list->first == NULL) { 
list->first = malloc(sizeof(list_element_t));
...

list->first相当于(*list).first,list指针的解引用更明显。

相关内容

最新更新