C语言中strdup()函数的目的是什么?
当前回答
就像它听起来一样,假设你习惯了C和UNIX分配单词的缩写方式,它复制了字符串:-)
请记住,它实际上不是当前(C17) ISO C标准本身的一部分(a)(它是POSIX的东西),它实际上与下面的代码相同:
char *strdup(const char *src) {
char *dst = malloc(strlen (src) + 1); // Space for length plus nul
if (dst == NULL) return NULL; // No memory
strcpy(dst, src); // Copy the characters
return dst; // Return the new string
}
换句话说:
It tries to allocate enough memory to hold the old string (plus a '\0' character to mark the end of the string). If the allocation failed, it sets errno to ENOMEM and returns NULL immediately. Setting of errno to ENOMEM is something malloc does in POSIX so we don't need to explicitly do it in our strdup. If you're not POSIX compliant, ISO C doesn't actually mandate the existence of ENOMEM so I haven't included that here(b). Otherwise the allocation worked so we copy the old string to the new string(c) and return the new address (which the caller is responsible for freeing at some point).
记住这是概念上的定义。任何有价值的库作者都可能针对所使用的特定处理器提供了大量优化的代码。
另一件需要记住的事情是,根据文档的N2912草案,这看起来是目前计划在标准的C2x迭代中与strndup一起出现。
(a)但是,以str和小写字母开头的函数被标准保留以备将来使用。7.1.3保留标识符:
每个标头声明或定义其关联子句中列出的所有标识符,并可选地声明或定义其关联的未来库方向子句中列出的标识符。*
String .h的未来方向可以在C11 7.31.13字符串处理< String .h>中找到:
以str、mem或wcs和小写字母开头的函数名可以添加到<string.h>头文件的声明中。
所以如果你想安全起见,你应该换个名字。
(b)如果(d == NULL)返回NULL,则更改基本上将被替换;:
if (d == NULL) {
errno = ENOMEM;
return NULL;
}
(c)注意,我使用strcpy,因为它清楚地显示了意图。在某些实现中,使用memcpy可能更快(因为您已经知道长度),因为它们允许以更大的块或并行方式传输数据。-)优化准则#1:“衡量,不要猜测”。
在任何情况下,如果你决定走这条路,你可以这样做:
char *strdup(const char *src) {
size_t len = strlen(src) + 1; // String plus '\0'
char *dst = malloc(len); // Allocate space
if (dst == NULL) return NULL; // No memory
memcpy (dst, src, len); // Copy the block
return dst; // Return the new string
}
其他回答
就像它听起来一样,假设你习惯了C和UNIX分配单词的缩写方式,它复制了字符串:-)
请记住,它实际上不是当前(C17) ISO C标准本身的一部分(a)(它是POSIX的东西),它实际上与下面的代码相同:
char *strdup(const char *src) {
char *dst = malloc(strlen (src) + 1); // Space for length plus nul
if (dst == NULL) return NULL; // No memory
strcpy(dst, src); // Copy the characters
return dst; // Return the new string
}
换句话说:
It tries to allocate enough memory to hold the old string (plus a '\0' character to mark the end of the string). If the allocation failed, it sets errno to ENOMEM and returns NULL immediately. Setting of errno to ENOMEM is something malloc does in POSIX so we don't need to explicitly do it in our strdup. If you're not POSIX compliant, ISO C doesn't actually mandate the existence of ENOMEM so I haven't included that here(b). Otherwise the allocation worked so we copy the old string to the new string(c) and return the new address (which the caller is responsible for freeing at some point).
记住这是概念上的定义。任何有价值的库作者都可能针对所使用的特定处理器提供了大量优化的代码。
另一件需要记住的事情是,根据文档的N2912草案,这看起来是目前计划在标准的C2x迭代中与strndup一起出现。
(a)但是,以str和小写字母开头的函数被标准保留以备将来使用。7.1.3保留标识符:
每个标头声明或定义其关联子句中列出的所有标识符,并可选地声明或定义其关联的未来库方向子句中列出的标识符。*
String .h的未来方向可以在C11 7.31.13字符串处理< String .h>中找到:
以str、mem或wcs和小写字母开头的函数名可以添加到<string.h>头文件的声明中。
所以如果你想安全起见,你应该换个名字。
(b)如果(d == NULL)返回NULL,则更改基本上将被替换;:
if (d == NULL) {
errno = ENOMEM;
return NULL;
}
(c)注意,我使用strcpy,因为它清楚地显示了意图。在某些实现中,使用memcpy可能更快(因为您已经知道长度),因为它们允许以更大的块或并行方式传输数据。-)优化准则#1:“衡量,不要猜测”。
在任何情况下,如果你决定走这条路,你可以这样做:
char *strdup(const char *src) {
size_t len = strlen(src) + 1; // String plus '\0'
char *dst = malloc(len); // Allocate space
if (dst == NULL) return NULL; // No memory
memcpy (dst, src, len); // Copy the block
return dst; // Return the new string
}
Strdup()对包含结束字符'\0'的字符数组进行动态内存分配,并返回堆内存地址:
char *strdup (const char *s)
{
char *p = malloc (strlen (s) + 1); // allocate memory
if (p != NULL)
strcpy (p,s); // copy string
return p; // return the memory
}
所以,它所做的是给我们另一个与它的参数所给出的字符串相同的字符串,而不需要我们分配内存。但我们仍然需要稍后释放它。
来自strdup man:
strdup()函数将返回一个指向新字符串的指针,该指针是s1所指向的字符串的副本。返回的指针可以传递给free()。如果不能创建新的字符串,则返回空指针。
它通过运行传入字符串的malloc和strcpy来复制传入的字符串。malloc'ed缓冲区返回给调用者,因此需要在返回值上自由运行。
声明:
strcpy(ptr2, ptr1);
等价于(除了会改变指针):
while(*ptr2++ = *ptr1++);
而:
ptr2 = strdup(ptr1);
等价于:
ptr2 = malloc(strlen(ptr1) + 1);
if (ptr2 != NULL) strcpy(ptr2, ptr1);
所以,如果你想让你复制的字符串被用在另一个函数中(因为它是在堆部分创建的),你可以使用strdup,否则strcpy就足够了,