strdup()-它在C中做什么?


302

strdup()C函数的目的是什么?


44
还有一个strdupa()(在GNU C库中),一个很好的函数,类似于strdup(),但是在堆栈上分配内存。您的程序不需要像strdup()那样显式地释放内存,当您退出调用strdupa()的函数时,它将自动释放
dmityugov,

11
strdupa是危险的,除非已确定strlen很小,否则不应该使用。但是,您可以仅在堆栈上使用固定大小的数组。
R .. GitHub停止帮助ICE,2010年

4
@slacker Google翻译是不是有帮助...这是什么strdup/ strdupa平均在波兰?
haneefmubarak 2015年

14
@haneefmubarak 在这里
anatolyg

Answers:


372

听起来确实如此,假设您已经习惯了C和UNIX分配单词的缩写方式,它将复制字符串 :-)

请记住,它实际上不是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
}

换一种说法:

  1. 它尝试分配足够的内存来容纳旧字符串(加上“ \ 0”字符以标记字符串的结尾)。

  2. 如果分配失败,它将设置errnoENOMEMNULL立即返回。在POSIX中,设置为errnoto ENOMEMmalloc必需的,因此我们不需要在中显式地进行设置strdup。如果您符合POSIX,则ISO C实际上并不强制存在,ENOMEM因此我没有在(b)中包括

  3. 否则分配将起作用,因此我们将旧字符串复制到新字符串(c)并返回新地址(调用者负责在某个时候释放地址)。

请记住,这是概念上的定义。任何物有所值的图书馆作家都可能针对使用的特定处理器提供了经过高度优化的代码。


(a)但是,str标准保留了以开头和小写字母开头的功能,以供将来使用。来自C11 7.1.3 Reserved identifiers

每个标头声明或定义在其关联的子条款中列出的所有标识符,并且*(可选)声明或定义在其关联的将来的库指示子条款中列出的标识符。**

有关的未来方向,请string.h参见C11 7.31.13 String handling <string.h>

str,,memwcs小写字母开头的函数名称可以添加到<string.h>标头中的声明中。

因此,如果您想安全起见,应该将其称为“别的东西”。


(b)这项变更基本上将由以下内容取代if (d == NULL) return 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
}

8
值得注意的是,正如Pax的示例实现所暗示的那样,strdup(NULL)是未定义的,并且不是可以预期的行为。
放松

2
另外,我认为malloc()会设置errno,因此您不必自己进行设置。我认为。
克里斯·卢茨

5
@Alcot,strdup用于需要为字符串副本分配堆内存的情况。否则,您必须自己做。如果您已经足够大的缓冲区(已分配内存或以其他方式分配),请使用strcpy
paxdiablo 2012年

2
@acgtyrant:按照标准,如果您指的是ISO标准(真正的C标准),则不,它不属于其中。它 POSIX标准的一部分。但是,尽管不是ISO C的正式组成部分,但仍有许多C 实现可提供此功能。但是,即使没有这样做,此答案中的五句也应该绰绰有余。
paxdiablo 2014年

2
好点,@ chux,ISO仅将其{ EDOM, EILSEQ, ERANGE }作为必需的错误代码。为此已更新了答案。
paxdiablo

86
char * strdup(const char * s)
{
  size_t len = 1+strlen(s);
  char *p = malloc(len);

  return p ? memcpy(p, s, len) : NULL;
}

也许代码比with快一点,strcpy()因为\0不需要再次搜索char(它已经与在一起strlen())。


谢谢。在我的个人实现中,我什至使其“更糟”。return memcpy(malloc(len), s, len);因为我更喜欢NULL分配失败而不是 分配失败。
PatrickSchlüter2010年

3
@tristopia取消引用NULL不必崩溃;它是未定义的。如果要确保它崩溃,请编写一个失败时emalloc调用abort
戴夫

我知道,但是可以保证我的实现只能在Solaris或Linux上运行(根据应用程序的本质)。
PatrickSchlüter

@tristopia:养成以最好的方式做事的习惯是很好的。emalloc即使没有在Solaris或Linux上使用它,也要养成使用的习惯,以便将来在其他平台上编写代码时使用它。
ArtOfWarfare 2015年

51

没有必要重复其他答案,但是请注意strdup(),由于它不是任何C标准的一部分,因此可以从C角度执行任何所需的操作。但是,它是由POSIX.1-2001定义的。


4
strdup()便携式?否,在非POSIX环境中不可用(无论如何还是可以实现)。但是说一个POSIX函数可以做任何事情都是很古怪的。POSIX是另一个标准这是好为C的,甚至更受欢迎。
PP

2
@BlueMoon我认为重点是声称不符合POSIX的C实现可能仍提供strdup扩展功能。在这种实现方式上,不能保证其strdup行为与POSIX函数相同。我不知道有任何这样的实现,但是char *strdup(char *)出于历史原因,合法的非恶意实现可能会提供,并拒绝传递的尝试const char *

C标准和POSIX有什么区别?用C标准表示,它在C标准库中不存在?
Koray Tugay,2015年

@KorayTugay它们是不同的标准。最好将它们视为无关的,除非您知道特定C函数的标准符合POSIX标准,并且您的编译器/库符合该函数的标准。

17

来自strdup man

strdup()函数应返回一个指向新字符串的指针,该字符串与所指向的字符串的副本相同s1。返回的指针可以传递给free()。如果无法创建新字符串,则返回空指针。


4

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
}

因此,它的工作是给我们另一个字符串,该字符串与其参数指定的字符串相同,而无需分配内存。但是,稍后我们仍然需要释放它。


3

它通过运行malloc和传入的字符串的strcpy来复制传入的字符串的副本。malloc的缓冲区返回给调用者,因此需要在返回值上自由运行。


3

strdup并且strndup在符合POSIX的系统中定义为:

char *strdup(const char *str);
char *strndup(const char *str, size_t len);

所述的strdup()函数分配用于字符串的副本足够的存储器str,莫非拷贝,并返回一个指向它的指针。

指针随后可以用作函数的参数free

如果没有足够的可用内存,则将其NULL返回并errno设置为 ENOMEM

strndup()最多功能复制len的字符从字符串str总是空终止复制的字符串。


1

它所做的最有价值的事情是给您另一个与第一个相同的字符串,而无需您自己分配内存(位置和大小)。但是,如前所述,您仍然需要释放它(但是也不需要进行数量计算。)


1

该声明:

strcpy(ptr2, ptr1);

等效于(除了更改指针的事实以外):

while(*ptr2++ = *ptr1++);

鉴于:

ptr2 = strdup(ptr1);

等效于:

ptr2 = malloc(strlen(ptr1) + 1);
if (ptr2 != NULL) strcpy(ptr2, ptr1);

因此,如果您希望复制的字符串在另一个函数中使用(因为它是在堆部分中创建的),则可以使用strdup,否则strcpy就足够了,


0

strdup()函数是字符串复制的简写,它接受一个参数作为字符串常量或字符串文字,并为该字符串分配足够的空间,并在分配的空间中写入相应的字符,最后返回分配的地址调用例程的空间。


1
的参数strdup不必是字符串常量,而必须是C字符串,即以终止的空数组char
chqrlie
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.