0

snprintf 和 strncpy 的细节区别

 3 years ago
source link: https://zhiqiang.org/coding/snprintf-strncpy.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

snprintf 和 strncpy 的细节区别

作者: 张志强

, 发表于 2020-11-06

, 共 1018 字 , 共阅读 147 次

现在一般不能用 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.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK