C语言 为什么我需要使用 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;
}

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

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

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

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

#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("%sn", 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 步:

然后将一个新单词,即"What!!"复制到str数组中,每个节点现在将再次打印str的内容,即"What!!"。

结语

这完全取决于您如何称呼newNode.

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

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

此行不会像您认为的那样执行:

n->word = word

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

n->word = strdup(word);

或者,为了符合标准:

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

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

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

这意味着您正在传递给函数 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

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

相关内容

  • 没有找到相关文章

最新更新