【问题标题】:Using strncpy() to copy const char *使用 strncpy() 复制 const char *
【发布时间】:2013-01-25 06:59:37
【问题描述】:

我对 C 很陌生,我在使用 strncpy 函数时遇到了困难。\

这是我正在使用的示例:

int main()
{

const char *s = "how";

struct test {
    char *name;
};

struct test *t1 = malloc(sizeof(struct test));

strncpy(t1->name, s, sizeof(*s));
t1->name[NAMESIZE] = '\0';

printf("%s\n", t1->name);

}

我有一个 const char *,我需要将 test 的“名称”值设置为 const char。我很难弄清楚这一点。这甚至是正确的方法吗?

非常感谢!

【问题讨论】:

  • @KerrekSB:说得很好:D

标签: c strncpy


【解决方案1】:

strncpy() 总是错

  • 如果结果太长,目标字符串将不会被空终止
  • 如果目标太长(第三个参数),尾端将完全用 NUL 填充。如果您有大缓冲区和短字符串,这将浪费很多周期。

相反,您可以使用 memcpy() 或 strcpy,(或者在您的情况下甚至是 strdup())

int main()
{
const char *s = "how";

struct test {
    char *name;
    };
struct test *t1
size_t len;

t1 = malloc(sizeof *t1);

#if USE_STRDUP

  t1->name = strdup(s);

#else

  len = strlen(s);
  t1->name = malloc (1+len);
  memcpy(t1->name, s, len);
  t1->name[len] = '\0';

#endif    

printf("%s\n", t1->name);

return 0;
}

【讨论】:

  • 应该限定:“strncpy 总是错误的对于动态分配的目的地”?
  • 没有。它总是错误的。由于您必须手动修复角盒,因此您也可以首先使用 memcpy。
  • 嗯。假设您总是将复制一个小缓冲区。要使用memcpy,您必须首先获取整个(可能很大)字符串的长度。使用strncpy,您可能会支付一些额外的不需要的零,但不会超过(短)目标长度。
  • FWIW snprintf(dest, dest_size, "%s", src);strncpy 的标准替代品。
【解决方案2】:

让我们一步一步来:

struct test *t1 = malloc(sizeof(struct test));

这为struct test分配空间;指针name 有足够的空间,但指向的指针没有任何内存。至少,您需要执行以下操作:

t1->name = malloc(strlen(s) + 1);

完成后,您可以继续复制字符串。但是,您已经计算了一次字符串的长度来分配内存;通过调用strncpy 再次隐式执行它是没有意义的。相反,请执行以下操作:

const size_t len = strlen(s) + 1;  // +1 accounts for terminating NUL
t1->name = malloc(len);
memcpy(t1->name, s, len);

一般来说,尽量使用这种基本模式; once 计算字符串进入代码时的长度,然后使用显式大小的内存缓冲区和 mem* 操作,而不是使用 str* 操作的隐式长度字符串。如果操作得当,它至少同样安全(而且通常更安全)和更有效。

如果t1->name 是一个固定大小的数组,您可以使用strncpy(尽管许多人更喜欢使用strlcpy)。如下所示:

struct test { char name[MAXSIZE]; };
struct test *t1 = malloc(sizeof *t1);
strncpy(t1->name, s, MAXSIZE - 1);
t1->name[MAXSIZE-1] = 0; // force NUL-termination

请注意,strncpysize 参数应始终是目标的大小,而不是源的大小,以避免写入目标缓冲区的范围之外。

【讨论】:

  • 非常感谢您的详尽解释,现在清楚多了。
  • 可以在strncpy 中调用MAXSIZE - 1“超越一切理由进行优化”吗? :-)
【解决方案3】:

在不尝试完整性或教育指导的情况下,这里有一个应该可以工作的代码版本。您可以在本网站上玩“spot the difference”并分别搜索每一个的解释。

int main()
{ 
    const char s[] = "how";                 // s is an array, const char[4]

    struct test{ char name[NAMESIZE]; };    // test::name is an array

    struct test * t1 = malloc(sizeof *t1);  // DRY

    strncpy(t1->name, s, NAMESIZE);         // size of the destination
    t1->name[NAMESIZE - 1] = '\0';          // because strncpy is evil

    printf("%s\n", t1->name);

    free(t1);                               // clean up
}

【讨论】:

  • 我不能随意更改结构定义。但是我意识到我在做 malloc(sizeof struct test) 而不是 sizeof *t1 的错误。谢谢
  • @isal:那么别忘了也为字符串分配内存并使用strlen 来计算这些字符串的大小。
  • @isal:如果您必须动态处理test::name,请参阅斯蒂芬的回答,但不要忘记单独清理。
【解决方案4】:

好吧,你分配了结构,而不是结构内部的字符串。在复制到它之前,您需要这样做。即使这样做,当您尝试设置字符串终止符时,也可能会覆盖未分配的内存。

而且,由于酒的摄入量很高,我刚刚注意到您实际上只复制了一个字符,但这仍然是未定义的行为

【讨论】:

  • 谢谢。我分配了 t1->name = malloc(sizeof(s)) 然后使用了 strncpy。它现在可以工作了,但是它说 strncpy 是 char 上的一个函数,但我使用的是 sizeof char *。
  • @isal sizeof(char*) 是 4 或 8 个字节,具体取决于您是在 32 位还是 64 位平台上。 sizeof(*s) 是 1,因为它与 C 规范中指定为等于 1 的 sizeof(char) 相同。您可能应该使用strlen(s) + 1
  • 你的酒似乎比我的威士忌更能代表你。这是真正的教训吗? :-S
  • 这个答案让我有点困惑,所以我向其他读者澄清。 malloc(sizeof(struct test)) 分配结构,包括其中的 char*,但它没有将内存分配到 char* 将指向的位置。当你想使用字符串本身(而不是指向它的指针)时,你需要为它分配新的内存。
  • @LastStar007 s 是指向char 的指针,取消引用s(这是*s 所做的)给你一个char,以及一个@987654331 的大小@ 始终是 1
猜你喜欢
  • 2011-01-18
  • 2015-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-15
  • 1970-01-01
  • 1970-01-01
  • 2012-12-26
相关资源
最近更新 更多