为什么要使用strncpy而不是strcpy?

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

我遇到这个例子 :

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之后,目标变成'123456789'

 destination1本来是='abcdefg'
在strncpy之后,destination1变成'12345fg'

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

strcpy() 上使用 strncpy() 什么好处

strncpy通过要求你输入一个长度来对抗缓冲区溢出。 strcpy依赖于\0 ,这可能并不总是会发生。

其次,为什么你选择只复制7个字符的5个字符串是超出我的,但它产生预期的行为。 它只复制前n字符,其中n是第三个参数。

n函数全部用作针对缓冲区溢出的防御编码。 请使用它们来代替旧的功能,如strcpy

strncpy()函数的设计考虑了一个非常特殊的问题:操作以原始UNIX目录条目的方式存储的字符串。 这些使用了一个固定大小的数组,并且只有在文件名比数组短的情况下才会使用一个空终止符。

这就是strncpy()的两个奇怪之处:

  • 如果目的地完全填满,它不会在目的地上放置一个终结者; 和
  • 它总是完全填补目的地,如果有必要的话。

对于“更安全的strcpy() ”,你最好使用像这样的strncat()

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

这将永远终止的结果,并不会超过必要的复制。

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

就个人而言,我的结论只是为了避免strncpy及其所有的朋友,如果你正在处理空终止的字符串。 尽管名称中有“str”,但这些函数不会生成以null结尾的字符串。 他们将空字符串转换为原始字符缓冲区。 在期望以空字符结尾的字符串作为第二个缓冲区的情况下使用它们是错误的。 如果源代码太长,你不但无法得到正确的空终止,而且如果源代码太短,你会得到不必要的空填充。

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

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

  • 如果你知道你的字符串和缓冲区的长度,为什么使用strncpy? 充其量是计算能力的浪费(加上无用的0)
  • 如果你不知道这个长度,那么你就会悄悄地截断你的字符串,这比缓冲区溢出要好得多

你正在寻找的是函数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; } 

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

同样对于像read()这样的函数,它不会将0终止在缓冲区中,但会返回读取的字节数,您可以手动将0或使用strncpy()复制。

在你的示例代码中, index实际上不是一个索引,而是一个count – 它告诉从源到目的地复制多少个字符。 如果在源的前n个字节中没有空字节,则放置在目标中的字符串将不会为空

strncpy用'\ 0'填充目的地的大小,尽管目的地的大小更小….

手册页:

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

而不仅仅是其余的…在此之后,直到达到n个字符。 因此你得到一个溢出…(见手册页的实现)

这可能会在许多其他情况下使用,只需将原始字符串的一部分复制到目标。 使用strncpy()可以复制原始字符串的有限部分,而不是通过strcpy()。 我看到你的代码来自publib.boulder.ibm.com 。

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

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

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

关于strcpy

关于strncpy

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