【问题标题】:Sending head of a linked list many times多次发送链表头
【发布时间】:2021-06-04 07:26:36
【问题描述】:

是否可以像这样将链表的头部传递两次?这适用于代码块,但不适用于在终端中编译时。我首先将 head 的地址传递给 test(),然后传递给 append()。我是完全迷路了还是有可能?


#include <stdio.h>
#include <stdlib.h>

struct Node {
  int data;
  struct Node* next;
};

void append(struct Node** head_ref, int new_data) {
  struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
  struct Node* last = *head_ref;
  new_node->data = new_data;
  new_node->next = NULL;

  if (*head_ref == NULL) {
    *head_ref = new_node;
    return;
  }

  while (last->next != NULL) last = last->next;

  last->next = new_node;
  return;
}

void printList(struct Node* node) {
  while (node != NULL) {
    printf(" %d ", node->data);
    node = node->next;
  }
}

void test(struct Node* p) { append(p, 6); }

int main() {
  struct Node* head = NULL;

  test(&head);
  // append(&head, 6);
  printList(head);
  return 0;
}

【问题讨论】:

  • 我想这段代码在编译时会产生许多警告和错误。例如,test() 的参数类型不正确。
  • 另外,请描述它是如何“不起作用”的。它会导致您的计算机着火吗?
  • 接近一个错字:test 应声明为void test(struct Node** p) { append(p, 6); },因为它获取并传递Node ** 而不是Node *。间接级别很重要...

标签: c linked-list pass-by-reference singly-linked-list function-definition


【解决方案1】:

正确来自

void test(struct Node* p) { append(p, 6); }

void test(struct Node** p) { append(p, 6); }

并始终使用以下标志进行编译:

gcc -Wall -Werror -Wextra -Wpedantic 

您会很快发现自己的错字,因此使用这些标志很重要:

head.c: In function ‘test’:
head.c:33:36: error: passing argument 1 of ‘append’ from incompatible pointer type [-Werror=incompatible-pointer-types]
 void test(struct Node* p) { append(p, 6); }
                                    ^
head.c:9:6: note: expected ‘struct Node **’ but argument is of type ‘struct Node *’
 void append(struct Node** head_ref, int new_data) {
      ^~~~~~
head.c: In function ‘main’:
head.c:38:8: error: passing argument 1 of ‘test’ from incompatible pointer type [-Werror=incompatible-pointer-types]
   test(&head);
        ^
head.c:33:6: note: expected ‘struct Node *’ but argument is of type ‘struct Node **’
 void test(struct Node* p) { append(p, 6); }
      ^~~~
cc1: all warnings being treated as errors

【讨论】:

    【解决方案2】:

    是否可以像这样将链表的头部传递两次?

    根据类型匹配和转换语义,函数将其参数值传递给它调用的其他函数是可能且合理的。所有的 C 参数都是按值传递的,值的意义并非来源于其来源。

    但是不,你不能这样做那样,因为你传递给test()的参数没有正确的类型(struct Node **而不是struct Node *)并且没有从一种标准自动转换到另一种。未定义的行为结果。在实践中,您可能会发现编译器接受了程序,并且程序仍然按照您的意愿运行,但依赖它是不安全的。希望编译器至少会发出关于类型不匹配的警告是合理的,如果没有,那么您应该研究使其发出更多警告的选项。

    另一方面,如果您更正了test() 的参数类型以匹配调用,那么调用test(&amp;head) 将等同于直接调用append(&amp;head, 6)

    【讨论】:

      【解决方案3】:

      函数test的参数类型为struct Node *

      void test(struct Node* p) { append(p, 6); }
      

      当您调用它时,传递struct Node ** 类型的表达式。

      test(&head);
      

      编译器将发出错误,指出使用了不兼容类型的指针。

      所以像这样改变函数声明

      void test(struct Node** p) { append(p, 6); }
      

      注意append函数有缺陷。它不报告是否为新节点分配内存成功。

      函数可以通过以下方式声明和定义

      int append( struct Node **head_ref, int new_data ) 
      {
          struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
          int success = new_node != NULL;
      
          if ( success )
          {
              new_node->data = new_data;
              new_node->next = NULL;
      
              while ( *head_ref != NULL ) head_ref = &( *head_ref )->next;
      
              *head_ref = new_node;
          }
        
          return success;
      }
      

      因此,函数test 可以通过以下方式声明和定义

      int test( struct Node **head_ref ) { return append( head_ref, 6); }
      

      函数printList 不会改变列表。所以它的参数应该有限定符const

      void printList( const struct Node *node ) 
      {
          for ( ; node != NULL; node = node->next ) 
          {
              printf( " %d ", node->data );
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-12
        • 2014-07-14
        • 1970-01-01
        • 1970-01-01
        • 2021-12-06
        相关资源
        最近更新 更多