strncpy()的最佳替代方法是什么?

函数strncpy()并不总是null终止所以我想知道什么是始终为null终止的最佳替代? 我想要一个函数,如果:

 strlen(src) >= n /*n is the number of characters to be copied from source*/ 

没有必要像这样添加更多代码:

 buf[sizeof(buf)-1] = 0; 

如果您想要复制的字符串的长度未知,则可以在此处使用snprintf 。 此函数将格式化输出发送到str 。 它与sprintf()类似,但不会写入str分配的更多字节。 如果结果字符串长于n-1字符,则剩余的字符将被省略。 它还始终包含空终止符\0 ,除非缓冲区大小为0

如果你真的不想使用它,这将是strncpy()strcpy()的替代品。 但是,使用strcpy()在字符串末尾手动添加空终止符始终是一种简单有效的方法。 在C中,在任何已处理的字符串的末尾添加空终止符是非常正常的。

以下是使用sprintf()的基本示例:

 #include  #include  #include  #define SIZE 1024 int main(void) { const size_t N = SIZE; char str[N]; const char *example = "Hello World"; snprintf(str, sizeof(str), "%s", example); printf("String = %s, Length = %zu\n", str, strlen(str)); return 0; } 

打印出来:

 String = Hello World, Length = 11 

此示例显示snprintf()"Hello World"复制到str ,并在末尾添加了\0终止符。

注意: strlen()仅适用于空终止字符串,如果字符串未终止,则会导致未定义的行为 。 snprintf()还需要更多错误检查,可以在手册页上找到。

正如其他人所说的那样,这不是一种有效的方法,但如果你去寻找它就会存在。

作为建议snprintf()的答案的替代方法:(注意:如果n <= 0

 size_t sz = sizeof buf; /*n is the number of characters to be copied from source*/ int n = (int) sz - 1; snprintf(buf, sz, "%s", src); 

代码可以使用以下精度

“......为s转换写入的最大字节数....”C11§7.21.6.14

 sprintf(buf, "%.*s", n, src); 

它有一个微妙的优点,因为src不必是一个字符串 ,只是一个字符数组。

字符串的另一个工具。

使用strlcpy()函数。 strlcpy()获取目标缓冲区的完整大小,如果有空间,则保证NULL终止。 阅读手册页以获取更多信息。

如果您想要的行为是strcpy的截断版本,它将源字符串的最长初始前缀复制到已知大小的缓冲区中,则有多个选项可供您使用:

  • 你可以写一个量身定制的function来完成这项工作:

     char *safe_strcpy(char *dest, size_t size, char *src) { if (size > 0) { size_t i; for (i = 0; i < size - 1 && src[i]; i++) { dest[i] = src[i]; } dest[i] = '\0'; } return dest; } 

    大多数BSD系统都有一个函数strlcpy(char *dest, const char *src, size_t n); 运作相同。 它的参数顺序令人困惑,因为ndest数组的大小,但是在src参数之后。

  • 你可以使用strncat()

     char *safe_strcpy(char *dest, size_t size, char *src) { if (size > 0) { *dest = '\0'; strncat(dest, src, n - 1); } return dest; } 
  • 您可以使用snprintf()sprintf() ,但感觉就像使用液压机打钉:

     snprintf(dest, size, "%s", src); 

    交替:

     if (size > 0) { sprintf(dest, "%.*s", (int)(size - 1), src); } 
  • 您可以使用strlen()memcpy() ,但只有在您知道源ptr指向空终止字符串时才可以使用。 如果源字符串比目标数组长得多,它的效率也低于上述两种解决方案:

     char *safe_strcpy(char *dest, size_t size, char *src) { if (size > 0) { size_t len = strlen(src); if (len >= size) len = size - 1; memcpy(dest, src, len); dest[len] = '\0'; } return dest; } 
  • 您可以使用strncpy()并强制null终止。 如果目标数组很大,这将是低效的,因为如果源字符串较短, strncpy()也填充目标数组的其余部分将为空字节。 这个函数的语义非常反直觉,很难理解,容易出错。 即使正确使用, strncpy()也是等待发生的错误,因为下一个程序员,更大胆但不太精明,可能会改变代码并引入它们以尝试优化他不理解的代码。 安全地玩:避免这个function。

为了说明必须使用strncpy()的替代方法,请考虑Git 2.19(Q3 2018),它发现滥用系统API函数(例如strcat())太容易了。 strncpy() ; …这些选定的函数现在在此代码库中被禁止,并将导致编译失败。

该补丁确实列出了几个替代方案,这使得它与这个问题相关。

请参阅提交e488b7a , 提交cc8fdae , 提交1b11b64 (2018年7月24日),并提交c8af66a (2018年7月26日)作者: Jeff King( peff ) 。
(由Junio C gitster合并- gitster -在提交e28daf2 ,2018年8月15日)

banned.h :将strncpy()标记为禁止

strncpy()函数比strcpy()更糟糕,但由于其有趣的终止语义,它仍然很容易被滥用。
也就是说,如果它截断它省略了NUL终结符,你必须记住自己添加它。 即使您正确使用它,读者有时也很难在不通过代码的情况下validation这一点。
如果您正在考虑使用它,请考虑:

  • strlcpy()如果你真的需要一个截断但NUL终止的字符串(我们提供一个compat版本,所以它总是可用的)
  • xsnprintf()如果您确定要复制的内容应该适合
  • strbufxstrfmt()如果需要处理任意长度的堆分配字符串。

请注意, compat/regex/regcomp.c中有一个strncpy实例,这很好(它在复制之前分配了一个足够大的字符串)。
但即使用NO_REGEX=1编译,这也不会触发禁止列表,因为:

  1. 编译时我们不使用git-compat-util.h(而是依赖于上游库中的系统包含); 和
  2. 它位于“ #ifdef DEBUG ”块中

因为它不会触发banned.h代码,所以我们最好不要让它保持与上游最小的分歧。

strcpy函数始终为null终止。 当然,您应该包含防止缓冲区溢出的代码,例如:

 char buf[50]; if (strlen(src) >= sizeof buf) { // do something else... } else strcpy(buf, src);