C 为什么我需要使用strdup()?

C 为什么我需要使用strdup()?,c,linked-list,copy,singly-linked-list,c-strings,C,Linked List,Copy,Singly Linked List,C Strings,在这段代码(单链表)中,如果我创建了许多节点,它们都有最后一个节点的名称,我需要理解为什么在newNode函数中,当我搜索解决方案时,我需要在这行代码n->word=strdup(word)中使用strdup()函数并在堆中创建word的副本 如果我使用malloc(sizeof(Node))这意味着在堆中为此节点保留一个位置,这样每个节点都应该独立,为什么它们共享最后一个节点的名称?因为word是指向字符串的指针,所以当malloc(sizeof(node))时,您只为指针分配空间,而不是为字

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


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

因为word是指向字符串的指针,所以当malloc(sizeof(node))时,您只为指针分配空间,而不是为字符串本身分配空间


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

这一行并不像您所想的那样:

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;
}
您需要使用
strdup()
(顺便说一句,从C18开始,它不是一个标准函数,但可能在C2x中)分别为字符串分配内存。上面的行只是复制字符串的地址,因此
n->word
word
指向同一个字符串。此行将创建一个具有相同内容的新字符串:

n->word = word
或者,为了符合本标准:

n->word = strdup(word);

这意味着您将传递给函数newNode

n->word = malloc((strlen(word) + 1) * sizeof(char));
strcpy(n->word, word);
指向同一字符数组的第一个字符的指针,该字符数组的内容在调用函数的代码中正在更改,但数组的地址没有更改,即使用同一数组

您需要创建一个字符串的副本,该字符串是传递给函数的指针。在这种情况下,函数将按以下方式看起来更复杂

Node* newNode(char *word) {

    Node *n = malloc(sizeof(Node));
    n->word = word;
    n->next = NULL;
    return n;
}
函数的调用方应检查获取的指针是否等于NULL

下面是一个简单的演示程序,演示如何使用函数将新节点附加到列表中。请注意,
strdup
函数不是标准的C函数

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;
}

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

也许这个例子可以帮助你理解

"Hello" -> "World" -> null
步骤1:

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

步骤2:

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

步骤3:

然后一个新词,即“What!!”被复制到
str
数组中,每个节点现在都会打印
str
的内容,即“What!!”

结论

这完全取决于如何调用
newNode

如果每次使用指向某个新内存的指针调用它,则无需将该单词复制到新位置(或使用
strdup


但如果调用<代码> NoNodo > NeNo结< /C>中复制其他内存(并且<<代码> StrupUp//Cuth>是复制该方法的一种方式)

基本上,C++中没有类型“字符串”。字符串是数组中对齐的一组字符。这意味着字符串是指针。因此,strdup允许您复制字符串的内容,而不复制该字符串的地址。

在这种情况下,您不一定需要
strdup
。这取决于调用
newNode
时传递给
word
的内容,以及您是否希望每次都指向相同的字符串缓冲区或新的字符串缓冲区。您的问题不清楚。。你说的“在堆上保留一个位置”是什么意思?“分享”呢?您如何使用
word
调用此函数?您可能每次都使用缓冲区调用
newNode
,例如
char myInput[100]。。。。。;新节点(myInput)
因此所有节点都指向该缓冲区。您需要发布如何调用
newNode
@PaulOgilvie我的意思是malloc为该指针提供了它所需的空间,共享意味着我创建的所有节点都具有与最后一个节点相同的单词node包含char*word,那么为什么它不也创建指向该单词的指针呢?这就是答案是,它只为指针分配内存。您需要为该指针分配为stringThis line
head=&(*head)->next分配的一些内存地址
我知道你必须传递双指针才能在main中更改
Node*head
,但我不明白这行是如何工作的。@ModyElSayed现在你正在传递指向下一个指针的指针。这与将指针传递到头节点的指针相同。也就是说,每个指针都通过引用传递,无论它是指向头节点的指针还是下一个数据成员的指针。
"Hello" -> "World" -> null
#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!!