何时使用strdup比使用malloc/strcpy好?

6

我可以使用mallocstrcpy来替换它吗?哪个更好?

例如:

char *s = "Global View";
char *d;
d = strdup(s);
free(d);

或者
char *s = "Global View";
char *d = malloc(strlen(s) +1);
strcpy(d,s);
free(d);

5
strdup函数可能避免了两次查找字符串长度的需要,并且在我看到它在代码中时更具描述性。不过这基于个人意见。 - Retired Ninja
当你使用纯C时,没有太大的区别,但确实有一些框架重新定义了strdup,例如PHP Zend。 - Frederick Zhang
6
strdup不是ISO C的一部分。 - M.M
3
编写可移植代码时应避免使用 strdup。自行实现该功能很简单,而且没有理由怀疑库的实现会更加高效,因为优化在于 strlenmemcpy - Lundin
2
@M.M,strdup 至少已经成为 POSIX 标准12年 - Andrew Henle
显示剩余4条评论
4个回答

9

哪个更好?

strdup(s);本身在分配失败时不会产生问题(调用代码仍需要处理NULL返回),不像下面的是未定义行为或UB。

char *d = malloc(strlen(s) +1);
strcpy(d,s); // should not be called if `d == NULL`.
< p > < code > strdup(s) 的典型实现不像替代方案那样两次遍历< code > s 的长度。

// 1st pass to find length of `s`
char *d = malloc(strlen(s) +1);
// Weak compiler/library may run 2nd pass to find length of `s` and then copy
strcpy(d,s);

一个好的 strdup(s) 函数会在长度需要时使用最佳复制代码进行一次遍历。可能是通过使用 memcpy() 或等效函数实现的。
关键在于 strdup() 预计经常使用,实现这个非标准 C 库函数的库应该被设计成能够最优化地执行。当有最好的工具可用时,请使用它。以下是一个示例实现:
#include <errno.h>
#include <stdlib.h>

char *my_strdup(const char *s) {
  if (s == NULL) { // Optional test, s should point to a string
    #ifdef EINVAL
      errno = EINVAL;  // For systems that support this "invalid argument" errno
    #endif
    return NULL;  
  }
  size_t siz = strlen(s) + 1;
  char *y = malloc(siz);
  if (y != NULL) {
    memcpy(y, s, siz);
  } else {
    #ifdef ENOMEM
      errno = ENOMEM;  // For systems that support this "out-of-memory" errno
    #else
      ;
    #endif
  }
  return y;
}

Rolling your own strdup() 与保留名称空间@Jonathan Leffler @Joshua 冲突。 malloc()/memcpy()/strcpy() 的一个重要优点是它们是标准的C库函数。虽然strdup()非常普遍,但它不在标准C库中。
[编辑] strdup()可能会出现在C2x中: Add strdup and strndup to C2X

1
事实上,标准甚至规定该名称专门用于此函数。这意味着库不应重新使用该名称表示其他含义。 - Joshua
2
标准本身并没有提到strdup()。它只是在§7.31.13字符串处理<string.h>中定义了未来库方向。_以小写字母开头的函数名,如strmemwcs,可以添加到<string.h>头文件的声明中。_这是一个笼统的规定,当然包括strdup(),但并没有将其从所有与模式str[a-z].*匹配的函数名中单独挑出来。 - Jonathan Leffler
@TomZych 问题在于strdup()本身在内存分配失败时不会导致未定义的行为。d = strdup(s); free(d);是可以的,无论是否出现内存不足的情况。在这个简单的例子中不需要检查。当分配失败时,OP的代码char *d = malloc(strlen(s) +1); strcpy(d,s);是UB。正如你所评论的那样,下面的代码如何使用strdup()的返回值非常重要。健壮的代码通常应该检查其返回值。 - chux - Reinstate Monica
今天有哪个主要平台没有strdup()strndup()作为系统库的一部分可用? - Dúthomhas
堆分配字符串,是的,任何严格遵循C语言标准的库都不包含strdup()函数。这是你在SO上发布的一个有趣问题。我也不会感到惊讶,如果在嵌入式编译器中根本找不到strdup()函数。 - chux - Reinstate Monica
显示剩余6条评论

1

除了strdup更简短之外,没有太大的区别。
strdup == malloc + strcpy


3
在手动操作时,调用 strcpy 之前应该真正检查 malloc 返回的结果。在使用 strdup 版本时,应该检查最终结果是否为NULL。 - BaseZen

1
使用strdup()可以在使用libc字符串处理函数时保持一致。 strdup()意味着操作数是libc的模型,即以空字符结尾的字符串。 libc的str...()函数完美地解决了C字符串处理的基本问题,因此只要它们足够使用,就应该使用它们,即使出于其他原因,也要让您的代码更容易被他人理解,并避免编写不必要的代码。
我个人不会没有强烈的理由混合使用模型。可能会出现需要使用自定义函数来补充libc字符串函数或完全绕过它们的情况,例如,不是所有C平台都提供libc。可能存在链接问题,或者您正在内核上下文中工作,无法访问libc而不崩溃或需要花费大量精力等等...
使用0或NULL表示文字值'\0'很诱人。大多数C程序员知道所有形式的NULL都可以工作。在相关场景中使用'\0'的优点是,它是一种简洁的方式来澄清您的意图。 '\0'代表一个字符,仅此而已。

0

我建议尽量使用strdup()函数。而malloc() + memcpy()只在字符串很大且你知道实际大小的情况下使用。这样可以节省计算字符串长度所需的时间。由于这是一个非常快速的函数,你只能在几兆字节的文本上才能感受到差异。 假设你有一个struct string结构体

 typedef struct string
 {
    char* st;
    long buf;
    long n;
 } String;

在这种情况下,您始终可以获得字符串的实际大小,因此如果我应该复制此字符串,我会写成:
 char * stringdup(String *st)
{
    char *res=malloc(st->n);
    res?memcpy(res,st->st,st->n):NULL;
} 

你可以注意到一些事情:当我复制字符串时,返回的是char*而不是String。经验表明,struct string仅适用于构建字符串,但不适合用于存储字符串。因为所有调试器都可以将char*显示为字符串,但没有人会知道你的struct。因此,一旦定义了最终字符串,最好离开struct。这样可以节省空间,并且调试速度更快。这也是解释为什么对于小文本,不值得使用这样的指令的原因。你永远不会欣赏它,但它会搞乱你的代码。
更新:
我忘记了另一种情况。当你只需要复制字符串的子集时,可以使用malloc() + memcpy()。strdup()和strcpy()会复制所有内容。但如果你只想要文本的一部分,应该使用这些指令。

1
关于 memcpy(res,st->st,1),为什么只复制一个字符到 res 中?不清楚代码为什么使用 long 类型来表示字符串的大小。建议改用 size_t - chux - Reinstate Monica
@chux,没错!*res = *(st->st) - 更快的代码 - 没有库调用。 - clearlight
是的,你是对的。我已经改正了它。另一种使用这种形式的情况是当你必须连接大量字符串时。因此,只有在密集使用它时,使用结构化字符串的努力才是合理的。@nerdist colony:这只复制第一个字符。 - jurhas

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