为啥strdup被认为是邪恶的

Posted

技术标签:

【中文标题】为啥strdup被认为是邪恶的【英文标题】:Why is strdup considered to be evil为什么strdup被认为是邪恶的 【发布时间】:2012-10-10 16:47:23 【问题描述】:

我看到一些海报说strdup 是邪恶的。对此是否有共识?我使用它时没有任何内疚感,也看不出它比使用malloc/memcpy 更糟糕的原因。

我认为唯一可能赢得strdup 声誉的是调用者可能会滥用它(例如,没有意识到他们必须释放返回的内存;尝试将strcat 放到strdup'ed 字符串的末尾)。但是,malloc'ed 字符串也不是没有被滥用的可能性。


感谢那些认为这个问题没有帮助的人的回复和道歉(投票结束)。总结这些回复,似乎没有普遍的感觉 strdup 本身是邪恶的,但普遍认为它可以像 C 的许多其他部分一样被不当或不安全地使用。

确实没有“正确”的答案,但为了接受一个,我接受了@nneoneo 的答案——它同样可能是@R.. 的答案。

【问题讨论】:

这个问题不是我之前的评论引起的吗? 参考Can a loop cause issues with assignment in C上的cmets。 @SethCarnegie 是的,但我在其他地方也看到过同样的情绪,这就是为什么我提出了一个问题,而不仅仅是问你。 【参考方案1】:

我能想到的两个原因:

    它不是严格意义上的 ANSI C,而是 POSIX。因此,一些编译器(例如 MSVC)不鼓励使用(MSVC 更喜欢 _strdup),并且技术上 C 标准可以用不同的语义定义自己的 strdup,因为 str 是保留前缀。因此,它的使用存在一些潜在的可移植性问题。 它隐藏了它的内存分配。大多数其他 str 函数不分配内存,因此用户可能会被误导(如您所说)认为不需要释放返回的字符串。

但是,除了这些点之外,我认为谨慎使用 strdup 是合理的,因为它可以减少代码重复并为常见习语提供一个很好的实现(例如 strdup("constant string") 以获得可变的、可返回的副本一个文字字符串)。

【讨论】:

strdup() 正在进入C2x Standard (Draft PDF)【参考方案2】:

我的回答是支持strdup,它并不比 C 中的任何其他函数差。

    POSIX 是一个标准,如果可移植性成为问题,strdup 实施起来并不难。

    如果有人花一点时间阅读手册页并了解strdup 的工作原理,是否释放strdup 分配的内存应该不是问题。如果一个人不明白一个函数是如何工作的,很可能这个人会搞砸一些事情,这适用于任何函数,而不仅仅是strdup

    在 C 中,内存和大多数其他东西都由程序员管理,所以 strdup 并不比忘记释放 malloc'ed 内存更糟糕,null 终止 一个字符串,在 scanf 中使用了不正确的格式字符串(并调用了未定义的行为),访问 dangling 指针等。

(我真的很想将此作为评论发布,但无法添加一条评论。因此,将其发布为答案)。

【讨论】:

【参考方案3】:

我还没有真正听说过 strdup 被描述为邪恶,但有些人不喜欢它的一些可能原因:

    它不是标准的 C(但在 POSIX 中)。但是我觉得这个理由很愚蠢,因为它几乎是一种添加到缺少它的系统的单行函数。 在所有地方盲目地复制字符串,而不是尽可能在原地使用它们,这会浪费时间和内存,并将失败案例引入可能没有失败的代码中。 当您确实需要一个字符串的副本时,很可能您实际上需要更多空间来修改或构建它,而 strdup 并没有给您。

【讨论】:

【参考方案4】:

我认为对 strdup 的大部分担忧来自于缓冲区溢出和格式不正确的字符串的安全问题。如果将非空终止字符串传递给 strdup,它可以分配一个未定义长度的字符串。我不知道这是否可以专门用于攻击,但总的来说,仅使用具有最大长度的字符串函数而不是单独依赖空字符是一种很好的安全编码实践。

【讨论】:

如果一个非空终止的字符串被传递给任何需要一个空终止字符串的函数,那么程序员就大错特错了。 然而它确实发生了,这就是当安全是一个严重问题时使用 strncpy 而不是 strcpy 的原因。这也可能发生在意外的用户输入或损坏的文件中。一般来说,最好的安全实践是依赖显式长度而不是字符串的空终止。 对于大多数实际用途,我不使用strncpy()。它不保证空终止。如果将 5 字节字复制到 20 KiB 缓冲区中,它还会写入 20475 个空值。这两种行为我都不能接受。通常,我确保知道字符串有多长并使用memmove() 或(偶尔)memcpy();我确实有复发并使用strcpy(),但前提是我知道有足够的空间。 (如果有什么安慰的话,strncat()strncpy() 差;我从不使用它!)如果我不知道字符串的最大长度,我就无法安全地操作它。我什至不知道它可以在哪里被截断。 根据定义,没有空终止符的 char 数组不是“字符串”。当您给它一个 http URL 而不是文件路径时,您不会期望 fopen() 工作。任何将普通 char 数组提供给需要字符串的函数的程序员都应该使用 RTFM 或不允许停留在任何生产代码的 100 米范围内。他们很可能还会忘记检查 malloc() 的返回是否为 NULL。 如果您担心字符串处理的安全性(您应该始终如此),那么最好不要在您的代码中抛出原始的str* 调用。编写一个处理所有典型问题并专门使用它的字符串处理库。当然,如果您在使用strlen() 而不是my_strlen() 时更关心诸如“性能考虑”之类的愚蠢事情,那么您将得到应得的。【参考方案5】:

很多人显然不知道,但我个人认为strdup邪恶有几个原因,

主要是它隐藏了分配。其他str* 函数和大多数其他标准函数之后不需要free,因此strdup 看起来足够无害,您可以忘记在它之后进行清理。 dmckee 建议将其添加到您需要清理的功能的心理列表中,但为什么呢?我认为与将两条中等长度的线减少为一条短线相比没有什么大的优势。

它总是在堆上分配内存,并且使用 C99(是 99?)VLA,你还有另一个理由只使用strcpy(你甚至不需要malloc)。你不能总是这样做,但如果可以的话,你应该这样做。

它不是 ISO 标准的一部分(但它是 POSIX 标准的一部分,感谢 Wiz),但这确实是一个小问题,因为 R.. 提到它可以很容易地添加。如果您编写可移植程序,我不确定您如何判断它是否已经定义...

这些当然是我自己的一些原因,没有其他人的。为了回答你的问题,据我所知没有达成共识。

如果您只是为自己编写程序并且您发现strdup 没问题,那么与编写一个可供不同技能水平和年龄的许多人阅读的程序相比,没有理由不使用它。

【讨论】:

您的第一点几乎抹黑了整个 C 语言?如果你不释放 strdup(),你就不会释放你自己的东西。为什么会有所不同?作为 VLA,尤其是在任意大小的字符串上,它会要求麻烦和未定义的行为,而不会发出警告。至于最后一个项目符号:它不是标准的:是的。它是 POSIX 标准的一部分。它不是 ISO C 标准的一部分——它对大多数人来说足够便携。 @Wiz 你自己的东西很引人注目,strdup 融入其中。这就是重点。感谢关于标准的观点。 我很想投反对票,因为我不同意你所说的大部分内容。我不会,但我会公开表示,我认为您的反对意见并不真正相关。考虑到人们在模拟 strdup() 时犯错的次数——通常是因为忘记为终止的 null 分配足够的空间——拥有一个库函数比让每个人都自己重新发明(7 行)函数要明智得多。 7 行?我一直认为它是一两个...char *new = malloc(strlen(old)+1); return new ? strcpy(new, old) : 0; @R.. memcpy 更好,不是吗? IE。 size_t len = strlen(old) + 1; char *new = malloc(len); return new ? memcpy(new, old, len) : 0;【参考方案6】:

我不喜欢 strdup 的原因是没有自然对的资源分配。让我们尝试一个愚蠢的游戏:我说malloc,你说free。我说open,你说close。我说create,你说destroy。我说strdup你说......?

实际上,strdup 的答案当然是free,为了明确这一点,函数最好命名为malloc_and_strcpy。但是许多 C 程序员不这么想,忘记了 strdup 需要它的相反或“结束”free 来解除分配。

根据我的经验,在调用strdup 的代码中发现内存泄漏是很常见的。这是一个奇怪的函数,它结合了strlenmallocstrcpy

【讨论】:

以上是关于为啥strdup被认为是邪恶的的主要内容,如果未能解决你的问题,请参考以下文章

strdup() - 它在 C 中做了啥?

C语言strdup函数

linux C函数之strdup函数分析

strcpy 与 strdup

内存资源 ( strdup )

strdup和strndup函数