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

时间:2017-01-26 08:55:36

标签: c strncpy

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

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

没有必要再添加这样的代码:

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

6 个答案:

答案 0 :(得分:6)

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

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

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#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()仅适用于空终止字符串,如果字符串未终止,则会导致undefined behavioursnprintf()还需要更多错误检查,可以在man page上找到。

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

答案 1 :(得分:3)

作为建议snprintf()的{​​{3}}的替代方法:(注意: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不必是字符串,只是一个字符数组。

字符串的另一种工具。

答案 2 :(得分:2)

使用strlcpy()功能。 strlcpy()获取目标缓冲区的完整大小,并保证在有空间时终止NULL。阅读man页面了解更多信息。

答案 3 :(得分:2)

如果你想要的行为是strcpy的截断版本,它将源字符串的最长初始前缀复制到已知大小的缓冲区中,那么有多种选择:

  • 你可以编写一个量身定制的功能来完成这项任务:

    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()并强制终止空值。如果目标数组很大,这将是低效的,因为如果源字符串较短,strncpy()也填充目标数组的其余部分将为空字节。这个函数的语义非常反直觉,很难理解,容易出错。即使正确使用,strncpy()的出现也是等待发生的错误,因为下一个程序员,更大胆但不太精明,可能会改变代码并引入它们以尝试优化他不理解的代码。安全地玩:避免这个功能。

答案 4 :(得分:1)

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

该补丁程序确实列出了几种选择,这使其与此问题相关。

请参见commit e488b7acommit cc8fdaecommit 1b11b64commit c8af66aJeff King (peff)(2018年7月26日)。
(由Junio C Hamano -- gitster --commit e28daf2中合并,2018年8月15日)

  

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

     

strncpy()函数比strcpy()可怕,但是由于其有趣的终止语义仍然很容易被误用。
  即,如果它被截断,它将忽略NUL终止符,并且您必须记住自己添加它。即使您正确使用它,有时读者也很难在不遍历代码的情况下进行验证。
  如果您正在考虑使用它,请考虑:

     
      
  • strlcpy(),如果您确实只需要一个截断但NUL终止的字符串(我们提供了一个兼容版本,因此它始终可用)
  •   
  • xsnprintf(),如果您确定要复制的内容合适
  •   
  • strbufxstrfmt(),如果您需要处理任意长度的堆分配字符串。
  •   
     

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

     
      
  1. 我们在编译时不使用git-compat-util.h(相反,我们依赖上游库中的系统包含);和
  2.   
  3. 它在一个“ #ifdef DEBUG”块中
  4.   
     

由于它不会触发banned.h代码,因此我们最好保留它,以使与上游的差异最小。


注意:一年多以后,在Git 2.21(2019年第一季度)中,“ strncat()”功能本身现在也 被禁止了。

请参见commit ace5707Eric Wong (ele828)(2019年1月2日)。
(由Junio C Hamano -- gitster --commit 81bf66b中合并,2019年1月18日)

  

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

     

strncat()具有与strcat()相同的二次行为,并且难以阅读且容易出错。尽管在Git iself中这还不是问题,但strncat()发现它已进入master的'cgit'中,并在我的系统上造成了段错误。

答案 5 :(得分:-1)

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

char buf[50];

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