96

编辑:我已经添加了示例的源代码。

我遇到了这个例子

char source[MAX] = "123456789";
char source1[MAX] = "123456789";
char destination[MAX] = "abcdefg";
char destination1[MAX] = "abcdefg";
char *return_string;
int index = 5;

/* This is how strcpy works */
printf("destination is originally = '%s'\n", destination);
return_string = strcpy(destination, source);
printf("after strcpy, dest becomes '%s'\n\n", destination);

/* This is how strncpy works */
printf( "destination1 is originally = '%s'\n", destination1 );
return_string = strncpy( destination1, source1, index );
printf( "After strncpy, destination1 becomes '%s'\n", destination1 );

产生了这个输出:

目的地最初是='abcdefg'
strcpy 后,destination 变为 '123456789'

destination1 最初是 = 'abcdefg'
strncpy 后,destination1 变为 '12345fg'

这让我想知道为什么有人会想要这种效果。看起来会很混乱。这个程序让我觉得你基本上可以用 Tom Bro763 复制某人的名字(例如 Tom Brokaw)。

使用 strncpy() over strcpy()有什么好处?

4

10 回答 10

194

strncpy()函数的设计考虑了一个非常特殊的问题:操作以原始 UNIX 目录条目的方式存储的字符串。这些使用了一个固定大小的短数组(14 字节),并且仅当文件名短于数组时才使用 nul 终止符。

这就是两个奇怪的背后的原因strncpy()

  • 如果完全填满,它不会在目的地上放置一个空终止符;和
  • 它总是完全填满目的地,必要时使用 nuls。

为了“更安全strcpy()”,你最好strncat()这样使用:

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

这将始终终止结果,并且不会复制不必要的内容。

于 2009-08-11T05:29:57.350 回答
101

strncpy通过要求您在其中输入一个长度来对抗缓冲区溢出。strcpy取决于尾随\0,这可能并不总是发生。

其次,为什么您选择仅在 7 个字符串上复制 5 个字符超出了我的理解,但它产生了预期的行为。它只是复制第一个n字符,n第三个参数在哪里。

这些n函数都用作针对缓冲区溢出的防御性编码。请使用它们代替较旧的功能,例如strcpy.

于 2009-08-11T05:21:29.473 回答
36

虽然我知道背后的意图strncpy,但它并不是一个很好的功能。避免两者。Raymond Chen 解释道

strncpy就个人而言,如果您正在处理以空字符结尾的字符串,我的结论就是要避免和所有它的朋友。尽管名称中有“str”,但这些函数不会产生以 null 结尾的字符串。他们将一个以 null 结尾的字符串转换为一个原始字符缓冲区。在预期以空结尾的字符串作为第二个缓冲区的地方使用它们是完全错误的。如果源太长,您不仅无法获得正确的空终止,而且如果源很短,您将获得不必要的空填充。

另请参阅为什么 strncpy 不安全?

于 2009-08-11T05:29:49.207 回答
27

strncpy 并不比 strcpy 更安全,它只是将一种错误与另一种错误交换。在 C 中,处理 C 字符串时,您需要知道缓冲区的大小,没有办法绕过它。strncpy 对于其他人提到的目录是合理的,但否则,你永远不应该使用它:

  • 如果您知道字符串和缓冲区的长度,为什么要使用 strncpy ?充其量就是浪费算力(加无用的0)
  • 如果您不知道长度,那么您可能会默默地截断您的字符串,这并不比缓冲区溢出好多少
于 2009-08-11T06:27:20.650 回答
23

您正在寻找的是strlcpy()始终以 0 终止字符串并初始化缓冲区的函数。它还能够检测溢出。唯一的问题是,它不是(真正)可移植的,并且仅存在于某些系统(BSD、Solaris)上。这个函数的问题是它打开了另一个蠕虫罐,从 http://en.wikipedia.org/wiki/Strlcpy的讨论中可以看出

strncpy()我个人的看法是,它比和有用得多strcpy()。它具有更好的性能,是snprintf(). 对于没有它的平台,它相对容易实现。(对于应用程序的开发阶段,我将这两个函数 (snprintf()strlcpy()) 替换为捕获版本,该版本会在缓冲区溢出或截断时残酷地中止程序。这可以快速捕获最严重的违规者。尤其是如果您使用其他人的代码库.

编辑:strlcpy()可以很容易地实现:

size_t strlcpy(char *dst, const char *src, size_t dstsize)
{
  size_t len = strlen(src);
  if(dstsize) {
    size_t bl = (len < dstsize-1 ? len : dstsize-1);
    ((char*)memcpy(dst, src, bl))[bl] = 0;
  }
  return len;
}
于 2009-08-11T07:59:11.480 回答
3

strncpy()函数更安全:您必须传递目标缓冲区可以接受的最大长度。否则,可能会发生源字符串未正确以 0 结尾的情况,在这种情况下,strcpy()函数可能会向目标写入更多字符,从而破坏目标缓冲区之后内存中的任何内容。这是许多漏洞利用中使用的缓冲区溢出问题

同样对于 POSIX API 函数read(),例如不将终止的 0 放入缓冲区,但返回读取的字节数,您将手动放入 0,或使用strncpy().

在您的示例代码中,index实际上不是索引,而是- 它告诉从源到目标最多count复制多少个字符。如果 source 的前 n 个字节中没有 null 字节,则放置在 destination 中的字符串不会以 null 结尾

于 2009-08-11T05:23:08.393 回答
1

strncpy 用 '\0' 填充目标源的大小,即使目标的大小更小....

手册页:

如果 src 的长度小于 n,strncpy() 用空字节填充 dest 的剩余部分。

不仅是剩余的......也在这之后直到达到 n 个字符。因此你得到一个溢出......(参见手册页实现)

于 2013-11-01T16:42:53.453 回答
-1

这可能用于许多其他场景,您只需将原始字符串的一部分复制到目的地。使用 strncpy(),您可以复制原始字符串的有限部分,而不是使用 strcpy()。我看到您提供的代码来自publib.boulder.ibm.com

于 2009-08-11T05:29:15.650 回答
-1

这取决于我们的要求。对于 Windows 用户

每当我们不想复制整个字符串或只想复制 n 个字符时,我们都会使用 strncpy。但是 strcpy 复制整个字符串,包括终止空字符。

这些链接将帮助您更多地了解 strcpy 和 strncpy 以及我们可以在哪里使用。

关于 strcpy

关于 strncpy

于 2016-05-24T11:12:52.787 回答
-8

strncpy 是 strcpy 的更安全版本,事实上你不应该使用 strcpy,因为它潜在的缓冲区溢出漏洞使你的系统容易受到各种攻击

于 2009-08-11T05:21:33.390 回答