snprintf 和 strncpy 的细节区别

作者: , 共 1018 字 , 共阅读 0

现在一般不能用 sprintf 和 strcpy ,推荐使用 snprintf 和 strncpy ,以防止缓冲区溢出:

char buffer[32];
snprintf(buffer, sizeof(buffer), "xxxxxxxxx");
strncpy(buffer, "xxxxxxx", sizeof(buffer));

但 snprintf 和 strncpy 有一些很细节的区别,一不注意就会出错:

  • 如果预复制(或打印)的字符串长度小于缓冲区长度(上面例子里的 32 ),那么 snprintf 会在末尾添加一个 0 ,而 strncpy 会在剩余的空间都填上 0。
  • 如果预复制(或打印)的字符串长度大于等于缓冲区长度(上面例子里的 32 ),那么 snprintf 会复制 31 个字符,然后填上一个 0 ,此时 buffer 是一个正常的 C 字符串,但比源字符串字符数要少。但 strncpy 会复制刚好 32 个字符,不会添加 0。此时 buffer 不是一个正常的 C 字符串,可能引起缓冲区溢出。最新的编译器会给出警告。
  • snprintf 返回打印的字符数(不包含尾部的 0 ), strncpy 返回 buffer (貌似多此一举)。

从防范风险的角度看, snprintf 更安全,但 snprintf 要慢很多。实际工作中需要取舍。

从 GCC9 开始,编译器会给 strncpy 给出一个-Wstringop-truncation的编译警告,提示可能没有 0 结束字符问题。

从实际工作角度,可以构造一个 sprintf 和 strncpy 的安全结合版:

inline char * safe_strncpy(char* dest, const char* src, size_t n)
{
    assert(n > 0);
    strncpy(dest, src, n - 1);
    dest[n - 1] = 0;

    return dest;
}

最后一点,如果确信源字符串长度不小于缓存区长度,可以直接用memset,效率更高(但注意最后没有添加 0 ):

char buffer[8];
memcpy(buffer, "xxxxxxxxx", sizeof(buffer)); 

Q. E. D.

类似文章:
编程 » C++, assert, 异常处理
1)在函数开始处检验传入参数的合法性
编程 » C++, folly
folly::fbstring是一个完全兼容std::string的类,可以做到无缝替换,而且性能更高。其代码参见https://github.com/facebook/folly/blob/master/folly/FBString.h
编程 » C++
假设在 C++里有一个数据结构:
编程 » C++, folly
高效程序总是尽量避免频繁触碰在堆上分配和释放内存,所以无论是std::string还是folly:fbstring都做了SSO( small string optimization )。而folly::FixedString是一个很有意思的实现,它可以把任意长度的字符串都放在堆上。代码可见https://github.com/facebook/folly/blob/master/folly/FixedString.h
出现的一个场景是将函数指针用 void
编程 » folly, C++, 数据容器
由 Facebook 开发和维护的 C++库 Folly 提供folly::small_vector,代码文件地址:https://github.com/facebook/folly/blob/master/folly/small_vector.h
编程 » C++, fmt
C++的fmt::format格式化库,由于效率高,功能强大,相关的 API 已经进入C++20,但其文档和 API 细节无法恭维。
后一篇:
编程 » pytorch
目前 pytorch 已经升级到了 1.7.0 ,但在 ubuntu 20.04 下有一个非常诡异的 bug。为此,我们只能自己编译。