C语言中的strncpy函数是否以空字符结尾

3

我正在阅读这篇文档,它说:

char *strncpy(char *destination, const char *source, size_t num);

Copy characters from string Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.

No null-character is implicitly appended at the end of destination if source is longer than num. Thus, in this case, destination shall not be considered a null terminated C string (reading it as such would overflow).

destination and source shall not overlap (see memmove for a safer alternative when overlapping).

但是我对这个声明感到困惑:

在这种情况下,目标不应被视为以空结束的C字符串(如果将其视为这样,会造成缓冲区溢出)

由于如果 num > strlen(source),它将在末尾填充 '\0''\0' 实际上是一个字符串中的空字符(终止符),那么为什么它不应该被视为以空结束的 C 字符串呢?

我编写了下面的代码进行验证:

  char from[] = { 'h', 'e', 'l', 'l', 'o', '\0' };
  char to[1024];
  for (int i = 0; i < 1024; i++) {
      to[i] = 'e';
  }
  strncpy(to, from, 1024);
  printf("from %s\n", from);

以下输出结果是正常的:

from hello
to hello

3
strncpy:不要使用它。 - chqrlie
缺少 '\0' 是导致 strlen(source) > num 的原因,所以我认为你只是有一个简单的误解 - 尝试使用 strncpy(to, from, 2); - Ken Y-N
如果您正在寻找一种在2023年替换strncpy的方法,那么可以使用std::string::copy。 - thd
3个回答

8

这段话讲述了当strlen(source)大于num时的情况。它只会复制num个字符,其中不包括空字符(NUL),也不会添加空字符(NUL)。


1
...并且为了确保你确实获得一个NUL终止符,请使用strlcpy而不是strncpy - Danra

7

strncpy(dst, src, len) 只有在 src 的前 len 个字节中有空字符时,才会向 dst 添加空字符。你的代码可能会看起来可行,因为数组 to[] 后面可能或可能没有空字符。更好的测试方法是:

char source[] = "source";
char dest[] = "destination";
strncpy(dest, source, 6);
printf("%s\n", dest);

结果应该是:
sourceation

如果你写成 strncpy(dest, source, 7),那么输出的结果就是单词 source

3
strncpy() 的语义即使在 C++ 参考文献中被准确地解释,也经常被误解。这个函数的行为是反直觉的,容易出错。
为了避免在使用它或在开发过程中出现问题,当维护者误读代码并添加更多微妙的错误时,有一个简单的解决方案:永远不要使用这个函数。
你可以在 Bruce Dawson 的这篇文章中阅读更多详细信息。
回答你的问题:如果源字符串比作为第三个参数传递的大小(通常对应于目标缓冲区的大小)长,则该函数将复制 size 个字符到目标位置,并且这些字符中没有空字节。调用 strlen(destination); 将会导致未定义的行为,因为它将尝试读取数组末尾之外的内容,直到找到空终止符。这种特定的行为使得 strncpy 容易出错。

@EvilTeach:我更新了答案。重要的是要防止新手使用这个容易出错的函数。我见过很多误用的情况,即使是有经验的程序员也一样...它就像一个普遍的错误吸引器。 - chqrlie
1
这是目前最好的答案。遗憾的是,“永远不要使用此函数”不能在红色和黑色之间闪烁。 - EvilTeach
我会将strtok、atoi、atof以及整个scanf函数族添加到应避免使用的函数列表中。 - EvilTeach
@EvilTeach: 我同意关于 strtokscanf 的看法。atoiatof 没有提供错误检查,但问题要少得多。feof() 也是列表中比较高的,sprintf() 应该被 snprintf() 替代,char 默认应该是无符号的,2 的补码是强制的,以及支持8/16/32/和64位整数类型,IEEE 浮点数... 这将使C变得简单许多。 - chqrlie
@EvilTeach: feof() 函数在 while (!feof(fp)) 中几乎总是被误用。默认情况下,char 是有符号的,这与 getc() 返回 unsigned char 类型的值加上 EOF 不一致,strcmp() 比较字符串作为 unsigned char 数组和 <ctype.h> 中的函数对负的 char 值调用未定义行为。 - chqrlie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接