【问题标题】:How to allocate the array before calling strcpy?如何在调用strcpy之前分配数组?
【发布时间】:2010-09-12 14:55:53
【问题描述】:

给定:

char test[] = "bla-bla-bla";

两者哪个更正确?

char *test1 = malloc(strlen(test));
strcpy(test1, test);

char *test1 = malloc(sizeof(test));
strcpy(test1, test);

【问题讨论】:

  • 注意:strlen(test) != sizeof test。区别在于1.终止的'\0'不计入strlen(),而是计入sizeof
  • 虽然 sizeof() 在这种情况下有效,但我不鼓励将其用于这种类型的操作,因为数组很容易衰减为指针,除非你小心,否则你可能会被这个抓住。
  • 为了内存边界安全,更喜欢 strncpy() 而不是 strcpy()

标签: c string


【解决方案1】:

这将适用于所有以 null 结尾的字符串,包括指向 char 数组的指针:

char test[] = "bla-bla-bla";
char *test1 = malloc(strlen(test) + 1);
strcpy(test1, test);

您将无法获得char*const char*sizeof 所指向的数组的正确大小。因此,此解决方案更加通用。

【讨论】:

  • strlen(test) + 1. strlen 不计算必要的 NULL。
  • 好的,如你所愿。我总是使用 sizeof(type) 来获得对可变操作系统更友好的代码。
  • @Tomasz:除了sizeof(char) 被定义为等于1 字节。至于里面的bits个数,不过……
  • 那么将 sizeof(char) 放入代码有什么问题呢?它使您尝试做的事情更加明确。因此,如果代码曾经更改为使用 wchar_t ,则更容易发现需要更改代码的位置。所以:虽然这里不需要,但我认为将它包含在代码中是一个好处,因为它使代码的含义更加精确。
  • 多年来我一直强烈反对写sizeof(char)。它增加了视觉上的混乱,C 中对象大小的语义从一开始就以sizeof(char) 为单位。这使得表达式在常见情况下变得清晰和简单,并引起对其他情况下对类型分配的依赖的关注。
【解决方案2】:

都不是:

#include <string.h>
char *mine = strdup(test);

【讨论】:

  • 这基本上等同于使用strlen 的版本 -- 在提供它的实现上。 当然,问题在于它是一个扩展,所以你可以'不依赖于它的存在。
  • 当然你可以很容易地写你自己的strdup如果它不见了。
  • strdup 未由标准定义。使用后需要拨打free(),所以还需要#include &lt;stdlib.h&gt;
  • @pmg:你需要#include &lt;stdlib.h&gt; 才能使用malloc,所以这已经是给定的了。
  • @R:虽然很简单,但如果你自己写strdup,你会得到未定义的行为;以str 开头的名称被保留。幸运的是,编写您的 dupe_string(或您喜欢的任何其他名称)同样容易。
【解决方案3】:

您应该使用strlen,因为如果您将 test 更改为运行时定义的字符串,sizeof 将静默失败。这意味着strlensizeof 安全得多,因为它会继续工作。

【讨论】:

  • 运行时变量也可以未初始化,strlen/strcpy会崩溃。
  • 至少在这种情况下你会遇到崩溃而不是默默地错误值。此外,编译器更容易警告您潜在的未初始化变量使用。最后,如果您传递一个未初始化的变量,那么您如何确定它的大小并不重要。
【解决方案4】:
char test[]="bla-bla-bla";
char *test1 = malloc(strlen(test) + 1); // +1 for the extra NULL character
strcpy(test1, test);

【讨论】:

  • 这个答案似乎与that重复
【解决方案5】:

我认为sizeof 是正确的。背后的原因是strlen(str) 会给你字符串的长度(不包括终止的空值)。如果您使用strcpy,它实际上会复制整个字符串,包括终止的null,因此如果您在malloc 中使用strlen,您将少分配一个字节。但是sizeof 给出了 test 所指向的字符串的大小,包括终止的 null,因此您将获得正确大小的 malloc 块来复制包含终止 null 的字符串。

【讨论】:

  • 如果您将 (char *) 对象传递给 sizeof() ,它将返回一个令人惊讶的“错误”结果。它将返回“存储指向'char'的指针所需的字节数”,不一定等于指针指向的字符串的大小。
  • +1:在显示的代码中,sizeof() 的解决方案是正确的,strlen() 的解决方案是不正确的。如果字符串在创建后被缩短,sizeof() 解决方案可能会过度分配内存。但是,数组的定义必须在其工作范围内 - 因此更一般的安全解决方案使用 strlen(str)+1,或者假设 POSIX 环境并使用 strdup()
【解决方案6】:

1) 肯定会导致 UB

2) 可能导致UB(如果malloc失败)

我会选择 2),因为该构造更有可能按预期工作;甚至更好的是,我会编写一个在所有情况下都能按预期工作(没有 UB)的版本。


编辑

  • 1) 中的未定义行为

    test1 将有空间用于test 中的字符,但不用于终止'\0'。对strcpy() 的调用将尝试将'\0' 写入不属于test1 的内存,因此是UB。

  • 2) 中的未定义行为

    如果对malloc() 的调用未能保留所请求的内存,则test1 将被分配NULL。将NULL 传递给strcpy() 调用UB。

应始终测试对malloc()(以及calloc() 和朋友)的调用的返回值,以确保操作按预期工作。

【讨论】:

  • 你知道,如果你能解释为什么 UB 参与其中(他没有处理 malloc 失败的事实),它可能会对 OP 有所帮助。
  • 其实这两种情况都是因为验证malloc()的返回失败导致的UB相同。第一种情况有未为 NUL 分配空间的奖励 UB。
【解决方案7】:

(1) 带有strlen 但不加 1 绝对是不正确的。如果你加 1,它还有一个额外的好处是它也适用于指针,而不仅仅是数组。

另一方面,只要您的字符串实际上是一个数组,就首选 (2),因为它会产生编译时常量,而不是调用 strlen(因此代码更快更小)。实际上,像gcc 这样的现代编译器如果知道字符串是常量,可能可以优化strlen,但编译器可能很难确定这一点,所以我总是尽可能使用sizeof

【讨论】:

  • 在您可以正确使用sizeof 的所有情况下,编译器发现可以优化strlen。所以根本没有理由使用sizeof
  • @Konrad: char test[] = "bla bla \0 bla";; strlen(test) == 8
  • @Konrad:不正确。字符串的内容可能是可变的,但总是适合一个固定大小的小数组。在这种情况下,strlen不能被优化掉,但是分配一个与数组大小相同的固定大小的对象总是合适的。
  • @pmg:这是一个完全不同的用例。我们在这里(含蓄地)谈论零分隔字符串,否则strlen 将毫无意义。在处理可能包含空字节的原始数据时,将该数据视为(零分隔)字符串显然是一个错误。
【解决方案8】:

如果它是关键路径,sizeofstrlen 具有优势,因为它具有 O(1) 复杂度,可以节省 CPU 周期。

【讨论】:

  • sizeof 是在编译时计算的,并且在每个地方 sizeof 都被编译器为此计算的整数替换。 sizeof 不再需要运行时计算,但 strlen 只解析一次完整的字符串。
猜你喜欢
  • 2020-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多