strdup() - C语言中它是什么作用?

330

strdup() 函数在C语言中的目的是什么?


46
还有一个叫做strdupa()的函数(在GNU C库中),它类似于strdup(),但是在堆栈上分配内存。与strdup()不同,您的程序无需显式释放内存,因为在调用strdupa()的函数退出时,它会自动释放。 - dmityugov
11
strdupa 很危险,除非您已确定 strlen 很小,否则不应使用。但是,这时您可以在栈上使用固定大小的数组来代替。 - R.. GitHub STOP HELPING ICE
4
"strdup"/"strdupa" 在波兰语中的意思是复制字符串。 - haneefmubarak
17
@haneefmubarak 在这里 - anatolyg
这里是strdup和strcpy之间的区别 https://dev59.com/IGYr5IYBdhLWcg3wAFvE - Siva Prakash
1
刚来这里补充一下,波兰语中的“dupa”和“string”都与屁股有关。 - Big Temp
11个回答

404

如果你已经习惯了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
}
换句话说:
1. 它试图分配足够的内存来容纳旧字符串(加上一个 '\0' 字符表示字符串的结尾)。
2. 如果分配失败,它会立即将errno设置为ENOMEM并返回NULL。在POSIX中,malloc会将errno设置为ENOMEM,因此我们不需要在strdup中显式执行此操作。如果您不遵循POSIX,则ISO C实际上并不强制要求存在ENOMEM,因此我没有在这里包含它。
3. 否则,分配成功,我们将旧字符串复制到新字符串中,并返回新地址(调用者在某个时候负责释放该地址)。
请记住,这是概念性定义。任何值得其薪水的库编写者可能提供了针对特定处理器进行了大量优化的代码。
还有一件事情要记住,看起来它目前被列入标准的C2x迭代中,连同strndup一起,根据文件草案N2912。
(a) 但是,以“str”和小写字母开头的函数被保留给标准的未来方向。 来自 C11 7.1.3 保留标识符:
每个头文件声明或定义其关联子条款中列出的所有标识符,并且可选地声明或定义其关联的未来库方向子条款中列出的标识符。*
string.h的未来方向可以在C11 7.31.13字符串处理中找到:
以“str”、“mem”或“wcs”和小写字母开头的函数名可能会添加到头文件的声明中。
因此,如果要安全起见,您应该将其命名为其他名称。
(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)是未定义的,你不能期望它以任何可预测的方式运作。 - unwind
2
另外,我认为malloc()会设置errno,所以你不应该自己设置。我想是这样的。 - Chris Lutz
5
@Alcot,“strdup”用于需要在堆内存中分配字符串副本的情况。否则,您需要自己完成这个过程。如果您已经有足够大的缓冲区(通过malloc分配或其他方式),那么可以使用“strcpy”。 - paxdiablo
2
@acgtyrant:如果你所说的标准是ISO标准(真正的C标准),那么不,它不是其中的一部分。它是POSIX标准的一部分。然而,有很多C的实现提供了它,尽管它不是ISO C的官方组成部分。但是,即使它们没有提供,这个答案中的五行代码应该已经足够了。 - paxdiablo
2
好观点,@chux,ISO仅规定 { EDOM,EILSEQ,ERANGE } 作为必需的错误代码。已更新答案以解决此问题。 - paxdiablo
显示剩余12条评论

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

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

由于\0字符已经被strlen()搜索过了,因此这段代码可能会比使用strcpy()稍微快一点。


谢谢。在我的个人实现中,我甚至让它变得更“糟糕”。return memcpy(malloc(len), s, len);因为我更喜欢在分配时崩溃而不是在分配失败时返回NULL - Patrick Schlüter
6
@derefencing NULL并不一定会崩溃;它是未定义的。如果你想确保它崩溃,可以编写一个调用abortemalloc - Dave
我知道,但我的实现只能在Solaris或Linux上运行(由于应用程序的本质)。 - Patrick Schlüter
1
@tristopia:养成做事最好的习惯是很好的。即使在Solaris或Linux上不需要,也要养成使用emalloc的习惯,这样当你在其他平台编写代码时就会使用它。 - ArtOfWarfare

53

不必重复其他答案,但请注意,strdup() 从C语言的角度来看可以执行任何操作,因为它不是任何C标准的一部分。然而,它确实由POSIX.1-2001定义。


7
strdup()是可移植的吗?不是,在非POSIX环境中不可用(但可以轻松实现)。但是,仅仅因为一个函数是POSIX函数,并不能说明它能做任何事情。POSIX是另一个标准,与C语言一样好甚至更受欢迎。 - P.P
2
@BlueMoon 我认为重点在于,声称不符合 POSIX 的 C 实现仍可以作为扩展提供 strdup 函数。在这样的实现中,不能保证 strdup 的行为方式与 POSIX 函数相同。我不知道是否有这样的实现,但是一个合法的非恶意实现可能出于历史原因提供 char *strdup(char *),并拒绝尝试传递 const char * - user743382
C标准和POSIX有什么区别?你所说的C标准是指它不存在于C标准库中吗? - Koray Tugay
@KorayTugay 它们是不同的标准。最好将它们视为不相关,除非您知道特定 C 函数的标准符合 POSIX 标准,并且您的编译器/库符合该函数的标准。 - Matthew Read

18

来自strdup函数手册:

strdup()函数将返回指向新字符串的指针,该字符串是由s1指向的字符串的副本。返回的指针可以传递给free()。如果无法创建新字符串,则返回空指针。


5

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
}

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


5

strdupstrndup在符合POSIX标准的系统中定义为:

char *strdup(const char *str);
char *strndup(const char *str, size_t len);
strdup()函数会为字符串str分配足够的内存,将其复制并返回指向该副本的指针。
该指针随后可用作free函数的参数。
如果内存不足,则返回NULL并将errno设置为ENOMEMstrndup()函数从字符串str中最多复制len个字符,并始终以空字符终止复制的字符串。

3

这个函数通过运行mallocstrcpy复制传入的字符串,然后返回malloc分配的缓冲区给调用者,因此需要对返回值运行free


2

这个语句是:

strcpy(ptr2, ptr1);

等价于(除了这会改变指针):

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

鉴于:

ptr2 = strdup(ptr1);

等同于:

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

所以,如果您想要将已复制的字符串用于另一个函数(因为它是在堆区创建的),则可以使用strdup,否则strcpy就足够了。

1
它最有价值的作用是为您提供另一个与第一个完全相同的字符串,而无需自己分配内存(位置和大小)。但是,正如注释所述,您仍然需要释放它(但不需要进行数量计算)。

0

strdup()函数是字符串复制的简写形式,它接受一个参数作为字符串常量或字符串字面值,并分配足够的空间来存储该字符串,并将相应的字符写入分配的空间,最后将分配的空间地址返回给调用程序。


2
strdup函数的参数不需要是字符串常量,它必须是C字符串,即以null结尾的char数组。 - chqrlie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接