【问题标题】:Insert at the back of a singly linked in C在 C 中单链接的后面插入
【发布时间】:2014-03-31 01:32:48
【问题描述】:

下面是我的节点结构声明和我在后面函数中的插入。在 main 中,我声明了一个头节点并将其指向 NULL。然后我调用该函数,并尝试打印出第一个节点的值,但我的程序停止了。我还是不知道哪里出了问题。

     typedef struct node {
         int val;
         struct node *next;
     } NODE;

     void insert_back(NODE *head, int val) {
     NODE *new = malloc(sizeof(NODE));
     new->val = val;
     new->next = NULL;
         if (head == NULL)
             head = new;
         else {
             NODE *p = head;
             while (p->next != NULL)
                 p = p->next;
             p->next = new;
         }
     }


     int main() {
         NODE *head = NULL;
         insert_back(head, 2);
         printf("%d", head->val);
     }

【问题讨论】:

标签: c insert singly-linked-list


【解决方案1】:

当您退出函数时,您在 insert_back 中分配的指针会丢失。为了让它工作,你的 insert_back 应该得到指针到指针。

typedef struct node {
         int val;
         struct node *next;
     } NODE;

     void insert_back(NODE **head, int val) {
     NODE *new = malloc(sizeof(NODE));
     new->val = val;
     new->next = NULL;
         if (*head == NULL)
             *head = new;
         else {
             NODE *p = *head;
             while (p->next != NULL)
                 p = p->next;
             p->next = new;
         }
     }


     int main() {
         NODE *head = NULL;
         insert_back(&head, 2);
         printf("%d", head->val);
     }

【讨论】:

  • @user2851034:请注意,还有另一种方法:返回指针同样有效,并且还有另一个好处:该函数也可以兼作自定义分配器。详情参考我的回答
  • @user2851034:虽然有点冗长,但我强烈建议您阅读我对答案的编辑/补充。我添加了一个示例函数,它将为您释放列表内存,并添加了 2 条您可能想要检查的建议
【解决方案2】:

您需要传递头部的地址而不仅仅是头部。而不是按值调用使用按引用调用

应该类似于insert_back(&head, 2);

并且定义更改为void insert_back(NODE **head, int val) {

【讨论】:

    【解决方案3】:

    C 传递所有内容按值。您将head 传递给insert_back 函数,它是一个空指针(如值NULL)。
    NULL 分配给该值的 head 参数变量。
    您正在更改 insert_back 函数的本地变量,这很好,但也不要期望更改 main 函数中的变量。

    有两种可能的方法:

    要么添加第二级间接(将指针传递给要更改的变量),要么返回 head 变量,然后重新分配:

    指针对指针:

    void insert_back(NODE **head, int val)
    {
        NODE *node = malloc(sizeof *node);
        if (node == NULL)//check if malloc was successful!
            exit(1);//or fprintf(stderr, "message"); and handle the issue
        node->val = val;
        node->next = NULL;
        if (*head == NULL)
        {
            *head = node;
            return;
        }
        NODE *tmp = *head;
        while (tmp->next != NULL)
            tmp = tmp->next;
        tmp->next = node;
    }
    

    像现在一样调用这个函数,但是传递指针的地址,而不是指针本身:

    NODE *head = malloc(sizeof *head);
    if (head == NULL) exit (1);
    head->next = NULL;
    insert_back(&head, 123);
    

    返回head

    NODE * insert_back(NODE *head, int val)
    {
        NODE *node = malloc(sizeof *node);
        if (node == NULL) exit (1);
        node->val = val;
        node->next = NULL;
        if (head == NULL)
        {
            return node;//head is null, no need to assign
        }
        NODE *tmp = head;
        while (tmp->next != NULL)
            tmp = tmp->next;
        tmp->next = node;
        return head;//return node passed initially, because it will be reassigned!
    }
    //call like so:
    head = insert_back(head, 123);
    

    作为一个额外的好处,你也可以使用这个函数来分配一个新的结构:

    NODE *head = insert_back(NULL, 123);//pass null pointer, will return new node and assign it to head
    

    但是,同样有效:

    NODE *head = insert_back(NULL, 123);
    head = insert_back(head, 456);
    head = insert_back(head, 789);
    printf("Head: %d\nNext: %d\nTail: %d\n",
           head->val,
           head->next->val,
           head->next->next->val
    );
    

    当然,别忘了写一个像样的函数来释放你的链表。
    也许,如果你还没有写过这个,这里有一个基本的例子(同样:两种方法都可以使用,但我建议使用指针到指针的方法):

    void free_list(NODE **list)
    {
        if (*list->next == NULL)
        {//tail
            free(*list);
            *list = NULL;//assign NULL, makes a valid NULL pointer
            return;
        }
        free_list(&(*list->next));//recursive call
        //once we get here, all next-nodes are freed:
        free(*list);//free node, and again:
        *list = NULL;//make a valid null pointer
    }
    //call:
    free_list(&head);
    free(head);//will not be a problem, head is NULL pointer
    

    或者:

    void * free_list(NODE *list)
    {//note VOID POINTER is returned (will always return NULL, though)
        if (list->next == NULL)
        {
            free(list);
            return NULL;
        }
        free_list(list->next);
        free(list);//free node, and again:
        return NULL;//make a valid null pointer
    }
    //call
    head = free_list(head);
    free(head);//not an issue here
    

    所以两者都同样安全,您可能会想,但是如果您忘记分配第二个 free_list 函数的返回值怎么办?

    free_list(head);
    free(head);//<--X undefined behaviour
    

    head 指向的内存已被释放,但您正在第二次调用free。这会让你很伤心:head 指针无效,将无效指针传递给free 会导致未定义的行为。这就是为什么第一种(指针对指针)方法是更安全的选择:该函数一旦编写,将永远不会忘记将 NULL 分配给指针。


    顺便提几点建议:

    您的 main 函数不返回 int。使用-Wall 编译此代码并通过添加return 0; 语句来解决该问题。
    查看所有函数的返回值,包括malloc&co,如果分配失败,则返回NULL。您没有对此进行检查,因此存在未定义行为的风险。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-10
      • 1970-01-01
      • 2020-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-20
      • 2020-12-11
      相关资源
      最近更新 更多