review同事代码时看到strncpy(dest, src, 64),他以为这样就万无一失不会溢出了,跑起来在边界条件直接少了个终止符,后面打印全是乱码。

strncpy本来想干嘛

它属于 string.h 头文件里的函数,原型是复制最多n个字节。很多人以为这是安全版strcpy,其实不是。当源字符串长度正好等于或超过n时,它不会补终止符,这就是最大的坑。

char dest[64]; strncpy(dest, src, sizeof(dest) - 1); dest[sizeof(dest) - 1] = 0;

两个反直觉行为

  • 源太长:不补零,得自己手动在末尾置零,否则后续函数读飞
  • 源太短:它用零填满剩余空间,看似贴心,实则是浪费,多一次清零开销
  • 返回值:返回的是dest指针,不能拿它判断有没有截断

想知道有没有被截断,得自己比较源字符串长度和n,长度一到n就说明危险了。

推荐用这两个替代

能用snprintf(dest, sizeof(dest), s)就用它,一定带终止符,出问题还知道被截断了。Linux和BSD上还有strlcpy,语义是保证终止,写起来更省心。

snprintf(dest, sizeof(dest), s);
strlcpy(dest, src, sizeof(dest));

如果手头的项目祖传代码全是strncpy,那么至少记住这个模板:长度参数永远用sizeof减一,下一行手动补零,绝不偷懒。

拷二进制别用它

strncpy遇到零字节就停,对二进制数据完全不能用,那种场景该memcpy出场,字符串场景再谈strncpy。

一句话取舍:新代码尽量别碰strncpy,能snprintf就snprintf,老代码沿用它时,手动补零那一行不能省。