【问题标题】:Using Linked Lists in C在 C 中使用链表
【发布时间】:2015-11-06 19:11:12
【问题描述】:

我正在学习 C 语言编程,但遇到了一些麻烦,尤其是在使用指针时。这对我来说有点困难,因为我们在 Java 或 C# 中不使用指针。

我尝试做的是创建一个链表(在互联网上找到的代码)并在其中推送元素。就像下面的代码一样。

发生的情况是,当我取消注释第二行代码时,代码有效,但我收到以下列表作为答案 {0, 1, 2},即使我没有在列表中推送数字 0。我想将其作为答案:{1, 2}。

int main (){
    node_t * test_list = NULL;

    //test_list = malloc(sizeof(node_t));

    push(test_list, 1);
    push(test_list, 2);

    print_list(test_list);

    return 0;
}

代码如下所示:

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

void print_list(node_t * head)
{
  node_t * current = head;

  while (current != NULL)
    {
      printf("%d\n", current->val);
      current = current->next;
    }
}

node_t* new_node(node_t * head)
{
  node_t * head2 = malloc(sizeof(node_t));

  return  head2;
}

void push(node_t * head, int val)
{
  if (head == NULL)
    {
      head = new_node(head);
      head->val = val;
      head->next = NULL;
    }
  else
    {
      node_t * current = head;
      while (current->next != NULL)
          current = current->next;

      /* now we can add a new variable */
      current->next = malloc(sizeof(node_t));
      current->next->val = val;
      current->next->next = NULL;
    }
}

在 push 函数中,我决定检查 head 是否等于 NULL。如果是这种情况,我只想创建一个新节点并将其分配给它。我不知道这是否是一个好方法。然而,它不起作用。

如果有人能引导我走上正确的道路,我将不胜感激!

谢谢!

(代码来源:http://www.learn-c.org/en/Linked_lists


【问题讨论】:

  • 您是否在调试器中逐行执行代码?您还尝试在 push() 中为 head 分配一个指针,但这是行不通的。 C 中的所有内容都是按值传递的。
  • ^^ 正确的路径是通向 gdb 或其他调试器。
  • push 中修改后的 head 没有找到返回给调用者的方法,因为您传递了一个 copy
  • 不,我没有使用调试器单步执行代码。但是,如果我从主函数中取消注释以下行 //test_list = malloc(sizeof(node_t));,它会在列表中添加一个额外的元素(元素 0),这是我不想要的。这就是为什么我试图让它与函数new_node(...)一起工作,但它不起作用。
  • 建议代码格式的一致性,以便我们人类可以轻松阅读/理解它。 1) 总是在每个左大括号 '{' 之后缩进 2) 在每个右大括号 '}' 之前总是不缩进 3) 永远不要使用制表符进行缩进,因为每个编辑器/文字处理器都有不同的制表符宽度/制表位。 4)在每个代码块周围插入一个空行 5)添加 cmets 以便阅读代码的人知道作者认为他们在做什么

标签: c pointers linked-list


【解决方案1】:

我将尝试解释额外元素 0。

test_list = malloc(sizeof(node_t)); //this changes the pointer test_list                
                                    //which is initially NULL to a non-NULL pointer, hence the mysterious extra element "0"

 push(test_list, 1);

void push(node_t * head, int val) {
if (head == NULL) //head is not NULL at this point because you called malloc earlier on it, so 1 will be inserted in the next position
{...}

你应该在 malloc 之后用一些值初始化列表的头部:

test_list = malloc(sizeof(node_t));


head->val = 5; //some value
head->next = NULL;

push(test_list, 1);
...

现在,第一个元素不会是 0,而是 5。

【讨论】:

  • 如果我在main函数中取消注释//test_list = malloc(sizeof(node_t));行,即使我不推送链表中的元素,也会出现神秘元素'0'。
  • 没错,因为 malloc 为您的 NULL 指针分配了一个值并将其初始化为 0,即使这不能保证(使用 calloc 进行 0 初始化)。
  • 另外,如果你仔细查看代码的源代码,你会发现,在 malloc 之后,结构被初始化为一些值:node_t * head = NULL; head = malloc(sizeof(node_t)); head->val = 1; head->next = NULL;
【解决方案2】:

当您在代码中取消注释以下行时

//test_list = malloc(sizeof(node_t));

在这里,您在调用推送函数之前分配头指针。 所以,下面的代码行永远不会被执行

 if (head == NULL)
    {
        head = new_node(head);
        head->val = val;
        head->next = NULL;}

因为你已经为头指针分配了一次并且你还没有初始化它,然后是两个推送函数。因此,您将在列表中看到 0/garbage,1,2 而不是 1,2。

当您为 test_list 注释了 malloc 后,在下面的代码中

if (head == NULL)
    {
        head = new_node(head);
        head->val = val;
        head->next = NULL;
    }else
    {

        node_t * current = head;
        while (current->next != NULL) {
            current = current->next;
        }

由于您没有发送 test_list 的地址(&test_list-您需要使用双指针),所以对if case 中的头部所做的任何更改都不会反映在 test_list 中。

浏览链接以获得清晰的理解- linked_list

【讨论】:

    【解决方案3】:

    尝试以下功能。

    node_t * new_node( int val )
    {
        node_t * n = malloc( sizeof( node_t ) );
    
        if ( n )
        {
            n->val = val;
            n->next = NULL;
        }
    
        return  n;
    }
    
    void push( node_t **head, int val ) 
    {
        node_t *n = new_node( val );
    
        if ( n )
        {
            while ( *head ) head = &( *head )->next;
            *head = n;
        }
    }
    

    函数推送必须像调用

    push( &test_list, 1 );
    

    至于您的函数push 实现,它处理test_list 的副本。所以test_list的原始值没有改变。

    【讨论】:

    • 鉴于他的代码目前的样子,我很确定如果没有更多完整的解决方案(即完整的使用示例),您的建议对他没有用处(即不是对您/您的回答的反映,而是对这个问题的广泛性质的反映)。
    • @mah 我所展示的足以解决将新元素填充到列表中的问题。所以我不明白你的评论的意义。
    • @VladfromMoscow 您的代码运行良好。非常感谢!
    【解决方案4】:

    来自莫斯科的 Vlad 和 Violeta Marin 已经发布了它不起作用的原因。我对在您的代码中进行了一些更改以使其起作用,

      #include<stdio.h>
      #include<stdlib.h>
    
      typedef struct node
      {
         int val;
         struct node * next;
      } node_t;
    
      void print_list(node_t * head) 
      {
          node_t * current = head;
          printf("called\n");
          while (current != NULL) {
             printf("%d\n", current->val);
             current = current->next;
          }
      }
    
    
      node_t* new_node()
      {
         //you have to return NULL , if malloc fails to allocate memory 
         node_t * head2 = malloc(sizeof(node_t));
         return  head2;
      }
    
      void push(node_t **head, int val)
      {
          if (*head == NULL)
          {
              printf("null\n");  
             //you have to check the return value of new_node
             (*head) = new_node();
             (*head)->val = val;
             (*head)->next = NULL;
          }
          else {
              printf("not null\n");
              node_t * current = *head;
    
              while (current->next != NULL) {
                  current = current->next;
              }
              /* now we can add a new variable */
              //you have to check the return value of malloc
              current->next = malloc(sizeof(node_t));
              current->next->val = val;
              current->next->next = NULL;
           }
      }
    
      void my_free(node_t *head)
      {
        node_t *temp= NULL;
        while(head) 
        {
           temp=head->next;
           free(head);
           head=temp;
        }
    }
    int main ()
    {
       node_t *test_list = NULL;
       push(&test_list, 1);
       push(&test_list, 2);
       print_list(test_list);
       my_free(test_list);
       return 0;
    }
    

    【讨论】:

      【解决方案5】:

      以下代码:

      1) corrects several oversights in the posted code
      2) works correctly
      3) contains the logic that (generally) is always used to 
         add a node to the end of a linked list
      4) demonstrates that to change the contents of a passed in pointer
         the simplest way is to pass '**' 
         and the caller passes the address of the pointer, not the contents of the pointer
      5) uses consistent formatting
      6) avoids unnecessary/misleading clutter/confusion in the definition of the struct
      7) removes unnecessary parameters from functions
      8) properly prototypes the functions 
         notice that the new_node() function prototype has '(void)'
         while the actual function body just has '()'
      
      #include <stdio.h>
      #include <stdlib.h>
      
      struct node
      {
          int val;
          struct node * next;
      };
      
      // prototypes
      void          print_list( struct node * head);
      struct node * new_node( void );
      void          push( struct node ** head, int val);
      
      
      int main ( void )
      {
      
          struct node * test_list = NULL;
      
          push(&test_list, 1);
          push(&test_list, 2);
      
          print_list(test_list);
      
          return 0;
      } // end function: main
      
      
      // step through linked list, printing the val field from each node
      void print_list( struct node * head)
      {
          struct node * current = head;
      
          while (current != NULL)
          {
              printf("%d\n", current->val);
              current = current->next;
          }
      } // end function: print_list
      
      
      // create a new node
      struct node * new_node()
      {
          struct node * head2 = NULL;
      
          if( NULL == (head2 = malloc(sizeof( struct node))))
          { // then malloc failed
              // handle error, cleanup, and exit
          }
      
          return  head2;
      } // end function: new_node
      
      
      // append a new node to end of linked list
      // need to use '**' so actual pointer in main() will be updated
      void push( struct node ** head, int val)
      {
          struct node * current = NULL;
      
          if (*head == NULL)
          { // then list is empty
              current = new_node();
              current->val = val;
              current->next = NULL;
              *head = current;
          }
      
          else
          {
              current = *head;
      
              while (current->next != NULL)
              {
                  current = current->next;
              }
      
              /* now we can append a new node to linked list */
              current->next = new_node();
              current->next->val = val;
              current->next->next = NULL;
          }
      } // end function: push
      

      【讨论】:

        猜你喜欢
        • 2013-02-21
        • 1970-01-01
        • 1970-01-01
        • 2020-09-01
        • 2012-05-24
        • 2019-03-04
        • 2017-08-16
        • 2020-01-16
        相关资源
        最近更新 更多