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

Kre*_*dns 77 c buffer-overflow c89 strcpy strncpy

编辑:我添加了示例的源代码.

我遇到了这个例子:

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 );
Run Code Online (Sandbox Code Playgroud)

哪个产生了这个输出:

destination is originally = 'abcdefg'
After strcpy, destination becomes '123456789'

destination1 is originally = 'abcdefg'
After strncpy, destination1 becomes '12345fg'

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

使用 strncpy() 过有 strcpy()什么好处

caf*_*caf 164

strncpy()函数的设计考虑了一个非常特殊的问题:操作以原始UNIX目录条目的方式存储的字符串.这些使用固定大小的数组,只有在文件名比数组短时才使用nul-terminator.

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

  • 如果它被完全填满,它不会在目的地上放置一个nul-terminator; 和
  • 如果有必要,它总是完全填满目的地.

对于"更安全strcpy()",你最好strncat()这样使用:

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

这将永远无法终止结果,并且不会复制超过必要的内容.

  • @snr:`strncat()` 将源字符串连接到目标字符串的末尾。我们只想将源字符串复制到目标,因此我们首先将目标设置为空字符串 - 这就是 `dest[0] = '\0';` 所做的。 (4认同)
  • 为什么我们需要在 strncat 调用之前编写 `dest[0] = '\0';`?先生介意解释一下吗? (3认同)

Eri*_*ric 92

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

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

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

  • 请参阅http://www.lysator.liu.se/c/rat/d11.html:`strncpy`最初被引入C库,以处理目录条目等结构中的固定长度名称字段.这些字段的使用方式与字符串不同:对于最大长度字段,尾部空值不是必需的,而将较短名称的尾随字节设置为空可确保有效的字段比较.**"strncpy"并非原产地是"有限的strcpy",委员会倾向于认识到现有的做法而不是改变功能以更好地适应这种用途.** (43认同)
  • 我不确定为什么这会得到很多投票 - strncpy从来没有打算作为strcpy的更安全的替代品,事实上并不是更安全,因为它不会将字符串定义为零.它还具有不同的功能,它用NUL字符填充提供的长度.正如caf在他的回复中所说 - 它是用于覆盖固定大小的数组中的字符串. (33认同)
  • 事实仍然是'strncpy`不是**'strcpy`的更安全版本. (22认同)
  • @Sinan:我从未说过它更安全.这是防守.它迫使你把它放在一个长度上,让你想想你在做什么.有更好的解决方案,但事实仍然是人们会(并且确实)使用`strncpy`而不是'strcpy`,因为它是一个更具防御性的功能......这就是我所说的. (7认同)
  • @chris&Sinan:它正在上升,因为问题是,"你为什么要使用strncpy而不是strcpy?" 不,"什么是strncpy?" 有一个明显的区别.这个答案解决了前者,而不是后者. (6认同)
  • *n个函数都用作防止缓冲区溢出的防御编码.请使用它们来代替旧函数,例如strcpy.*这对于`snprintf`来说是正确的,但与`strncat`无关,对于`strncpy`则完全不真实.这个答案怎么会得到这么多的赞成?它显示了这种虚假功能的情况有多糟糕.使用它并不是防御性的:在大多数情况下,程序员不理解它的语义并创建一个可能非零终止的字符串. (5认同)
  • @Eric你没有提到一个明显的事实,即`strncpy`会使目标字符串无法终止(如果源字符串长度大于或等于`n`).因此,它与防御性正好相反,它只是在乞求一个段错误. (4认同)

Sin*_*nür 31

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

就个人而言,我的结论只是避免strncpy及其所有朋友,如果你正在处理以null结尾的字符串.尽管名称中包含"str",但这些函数不会生成以null结尾的字符串.它们将以null结尾的字符串转换为原始字符缓冲区.使用它们作为第二个缓冲区的空终止字符串是明显错误的.如果源太长,您不仅无法获得正确的空终止,而且如果源很短,则会得到不必要的空填充.

另见为什么strncpy不安全?


Dav*_*eau 27

strncpy并不比strcpy更安全,它只是将一种类型的错误与另一种错误进行交易.在C中,当处理C字符串时,您需要知道缓冲区的大小,没有办法解决它.strncpy对于其他人提到的目录事物是合理的,但除此之外,你永远不应该使用它:

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

  • 将数据存储在固定格式文件中时,零填充字符串非常常见。可以肯定的是,数据库引擎和 XML 之类的东西的流行,以及不断变化的用户期望,已经导致固定格式文件不像 20 年前那样普遍。尽管如此,此类文件通常是最省时的数据存储方式。除非记录中数据的预期长度和最大长度之间存在巨大差异,否则将记录作为包含一些未使用数据的单个块读取要比读取分成多个块的记录快得多。 (2认同)

Pat*_*ter 21

你要找的是一个函数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;
}
Run Code Online (Sandbox Code Playgroud)

  • 您可以写一下strlcpy几乎可用于Linux和Windows以外的所有内容!但是,它是BSD许可的,所以你可以将它放入你的一个库中并从那里使用它. (3认同)