snprintf 和 strncpy 的细节区别

作者: , 共 1016 字 , 共阅读 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;
}

最后一点,如果确信源字符串长度不小于缓存区长度,可以直接用memcpy,效率更高(但注意最后没有添加 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++, fmt
最近遇到一个诡异的问题,一个程序经常卡死。最后定位到 fmt 和下面简单的例子:
编程 » C++
如果我们用std::getline或者简单的std::cin >>获取用户输入,有一个问题是,它会阻塞掉整个程序,用户必须有输入后才能继续执行。如果这个输入是单独的线程,它还会阻止整个程序的退出。
编程 » C++
在实现C++中非阻塞式的用户输入中发现,在没有设置in.sync_with_stdio(false)时,in.rdbuf()里面总是空的。
编程 » C++, popen
popen函数可以获取比std::system函数更详细的程序输出。只是正常调用 popen 只能获取 stdout 的输出,而 stderr 的输出被忽略。
出现的一个场景是将函数指针用 void
编程 » Python
在 Python3 以上,通常说的字符串是指unicode字符串,以下将不再重复强调。
编程 » C++, folly
高效程序总是尽量避免频繁触碰在堆上分配和释放内存,所以无论是std::string还是folly:fbstring都做了SSO( small string optimization )。而folly::FixedString是一个很有意思的实现,它可以把任意长度的字符串都放在堆上。代码可见https://github.com/facebook/folly/blob/master/folly/FixedString.h
编程 » C++, fmt
C++的fmt::format格式化库,由于效率高,功能强大,相关的 API 已经进入C++20,但其文档和 API 细节无法恭维。
和绿野的队伍一起去走石峡关野长城,结果走到长城上,风实在太大了,准备不周,孩子肚子疼,我们直接从花家窑子关隘沿大路回撤。轨迹已上传到两步路