【问题标题】:why do I need to use strdup()? [closed]为什么我需要使用 strdup()? [关闭]
【发布时间】:2020-08-28 05:02:38
【问题描述】:
typedef struct Node {
    char *word;
    struct Node *next;
} Node;

Node* newNode(char *word) {

    Node *n = malloc(sizeof(Node));
    n->word = word;
    n->next = NULL;
    return n;
}

在这段代码(单链表)中,如果我创建了许多节点,它们都有最后一个节点的名称,我需要了解为什么在 newNode 函数中我需要使用 strdup() 函数,当我搜索对于解决方案,在这行代码中 n->word = strdup(word); 并在堆中创建 word 的副本。

如果我使用malloc(sizeof(Node));,这意味着在堆中为该节点保留一个位置,这样每个节点都应该是独立的,为什么它们共享最后一个节点的名称?

【问题讨论】:

  • 在这种情况下,您不一定需要strdup。这取决于您在调用 newNode 时为 word 传递的内容,以及您是否希望它每次都指向同一个字符串缓冲区或一个新缓冲区。
  • 您的问题不清楚..“在堆上保留一个位置”是什么意思?和“分享”?以及如何用word 调用这个函数?
  • 您可能每次都使用缓冲区调用newNode,即像char myInput[100]; .....; newNode(myInput) 这样您的所有节点都指向该缓冲区。
  • 你需要发布你如何称呼newNode
  • @PaulOgilvie 我的意思是 malloc 为该指针提供了它需要的空间并共享意味着我创建的所有节点都与最后一个节点具有相同的单词

标签: c linked-list copy singly-linked-list c-strings


【解决方案1】:

您的节点仅包含一个指针,该指针需要指向内存中存储实际单词的某个位置。

也许这个例子会帮助你理解。

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

typedef struct Node {
    char *word;
    struct Node *next;
} Node;

Node* newNode(char *word) 
{
    Node *n = malloc(sizeof(Node));
    n->word = word;
    n->next = NULL;
    return n;
}

Node* insertNode(Node* head, Node* n) 
{
    n->next = head;
    return n;
}

void printList(Node* head)
{
    while(head)
    {
        printf("%s\n", head->word);
        head = head->next;
    }
}

int main(void) {

    // Step 1: Create a list that "works" due to use of string literals
    Node* head1 = NULL;
    head1 = insertNode(head1, newNode("world"));
    head1 = insertNode(head1, newNode("hello"));
    head1 = insertNode(head1, newNode("test"));
    printList(head1);

    printf("------------------------------------------------\n");

    // Step 2: Create a list that "fails" due to use of a char array
    Node* head2 = NULL;
    char str[20];
    strcpy(str, "test");
    head2 = insertNode(head2, newNode(str));
    strcpy(str, "hello");
    head2 = insertNode(head2, newNode(str));
    strcpy(str, "world");
    head2 = insertNode(head2, newNode(str));
    printList(head2);

    printf("------------------------------------------------\n");

    // Step 3: Change the value that head2 nodes points to
    strcpy(str, "What!!");
    printList(head2);

    return 0;
}

输出:

test
hello
world
------------------------------------------------
world
world
world
------------------------------------------------
What!!
What!!
What!!

第 1 步:

head1 列表按预期工作,因为每个节点都使用指向存储在内存中某处的字符串文字的指针进行初始化。每个字符串文字都存储在不同的内存中。因此它工作正常。

第 2 步:

head2 列表未按预期工作。这是因为每个节点都用str 初始化,所以所有节点都简单地指向str 数组。因此,所有节点都指向“世界”,即复制到str 中的最后一个单词。

第 3 步:

然后是一个新词,即“什么!!”被复制到str 数组中,每个节点现在都将再次打印str 的内容,即“什么!!”。

结论

这完全取决于你如何称呼newNode

如果你每次都用一个指向某个新内存的指针来调用它,你就不需要把这个词复制到一个新的位置(或者使用strdup)。

但是,如果您在调用 newNode 时重用缓冲区,则需要复制到 newNode 内的其他内存(strdup 是进行该复制的一种方法)

【讨论】:

    【解决方案2】:

    基本上,在 C++ 中没有“字符串”类型。字符串是一堆在数组中对齐的字符。这意味着字符串是一个指针。所以 strdup 让你复制字符串的内容,而不复制那个字符串的地址。

    【讨论】:

      【解决方案3】:

      这意味着你正在传递给函数 newNode

      Node* newNode(char *word) {
      
          Node *n = malloc(sizeof(Node));
          n->word = word;
          n->next = NULL;
          return n;
      }
      

      指向同一字符数组的第一个字符的指针,其内容在调用函数的代码中发生了更改,但数组的地址没有更改,即您使用的是同一数组。

      您需要复制传递给函数的指针的字符串。在这种情况下,下面的函数看起来会更复杂

      Node* newNode( const char *word ) 
      {
          Node *n = malloc( sizeof( Node ) );
          int success = n != NULL;
      
          if ( success )
          {
              n->word = malloc( strlen( word ) + 1 );
              success = n->word != NULL;
      
              if ( success )
              {
                  strcpy( n->word, word ); 
                  n->next = NULL;
              }
              else
              {
                  free( n );
                  n = NULL;
              }
          }
      
          return n;
      }
      

      函数的调用者应该检查获得的指针是否等于NULL或不等于NULL。

      这是一个简单的演示程序,展示了如何使用该函数将新节点添加到列表中。注意strdup这个函数不是标准的C函数。

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      typedef struct Node {
          char *word;
          struct Node *next;
      } Node;
      
      Node* newNode( const char *word ) 
      {
          Node *n = malloc( sizeof( Node ) );
          int success = n != NULL;
      
          if ( success )
          {
              n->word = malloc( strlen( word ) + 1 );
              success = n->word != NULL;
      
              if ( success )
              {
                  strcpy( n->word, word ); 
                  n->next = NULL;
              }
              else
              {
                  free( n );
                  n = NULL;
              }
          }
      
          return n;
      }
      
      int append( Node **head, const char *word )
      {
          Node *new_node = newNode( word );
          int success = new_node != NULL;
      
          if ( success )
          {
              while ( *head != NULL ) head = &( *head )->next;
      
              *head = new_node;
          }
      
          return success;
      }
      
      void display( Node *head )
      {
          for ( ; head != NULL; head = head->next )
          {
              printf( "\"%s\" -> ", head->word );
          }
      
          puts( "null" );
      }
      
      int main(void) 
      {
          Node *head = NULL;
          const char *word = "Hello";
      
          append( &head, word );
      
          word = "World";
      
          append( &head, word );
      
          display( head );
      
          return 0;
      }
      

      程序输出是

      "Hello" -> "World" -> null
      

      【讨论】:

      • 这条线head = &amp;( *head )-&gt;next; 我知道你必须通过双指针来改变主线中的Node *head,但我不明白这条线是如何工作的。
      • @ModyElSayed 现在您将指针传递给下一个指针。这与将指针传递给指向头节点的指针相同。即每个指针都是通过引用传递的,无论是指向头节点的指针还是下一个数据成员。
      【解决方案4】:

      这行不符合你的想法:

      n->word = word
      

      您需要使用strdup()(顺便说一下,它不是 C18 的标准函数,但可能在 C2x 中)为字符串单独分配内存。上面一行只是简单地复制了字符串的地址,所以n-&gt;wordword 指向同一个字符串。此行创建一个具有相同内容的新字符串:

      n->word = strdup(word);
      

      或者,为了符合标准:

      n->word = malloc((strlen(word) + 1) * sizeof(char));
      strcpy(n->word, word);
      

      【讨论】:

        【解决方案5】:

        因为 word 是一个指向字符串的指针,所以当 malloc(sizeof(Node)) 你只是为指针分配空间,而不是为字符串本身分配空间。

        这就是为什么你必须分别初始化 n->word(注意 strdup() 为你做了两件事:它分配内存并将字符串复制到其中,然后返回指针)。

        【讨论】:

        • 节点包含 char *word 那么为什么它不创建指向该词的指针呢?
        • 这就是答案所说的,它为指针分配内存。您需要为该指针分配为字符串分配的一些内存的地址
        猜你喜欢
        • 1970-01-01
        • 2011-05-27
        • 1970-01-01
        • 2013-11-20
        • 1970-01-01
        • 1970-01-01
        • 2013-09-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多