C语言的库函数中有很多老破旧是不检查越界的。所以后来出了很多函数名带n的替代品。

snprintf

不仅加上了n还会自动给补0。
函数声明:

  1. int snprintf(char * restrict str, size_t size, const char * restrict format, ...);

代码示例

  1. #include <iostream>
  2. #include <string>
  3. #include <stdio.h>
  4. #include <string.h>
  5. using namespace std;
  6. int main()
  7. {
  8. char sz[100];
  9. string str = "abcd";
  10. cout<<sizeof(sz)<<endl; // 100
  11. // str.c_str() 等于 str.data()
  12. cout<<sizeof(str.c_str())<<endl; // 8
  13. cout<<strlen(str.c_str())<<endl; // 4
  14. // str.size() 等于 str.length()
  15. cout<<str.size()<<endl; // 4
  16. int ia = snprintf(sz, sizeof(str.c_str()), "%s", str.c_str());
  17. cout<<sz<<endl; // abcd
  18. cout<<sizeof(sz)<<endl; // 100
  19. int ib = snprintf(sz, strlen(str.c_str()), "%s", str.c_str());
  20. cout<<sz<<endl; // abc
  21. cout<<ia<<endl; // 4
  22. cout<<ib<<endl; // 4
  23. return 0;
  24. }

可以看出 strlen(str.c_str()) 等价于 str.size() 都是实际字符的大小。
但是snprintf的中的参数size(第二个参数)需要比这个数字大。
snprintf默认拷贝size-1个字符,然后会给第size的字符赋值 ‘\0’。

  1. #include <iostream>
  2. #include <string>
  3. #include <stdio.h>
  4. #include <string.h>
  5. using namespace std;
  6. int main()
  7. {
  8. char sz[3];
  9. string str = "abcd";
  10. cout<<sizeof(sz)<<endl; // 3
  11. int ia = snprintf(sz, sizeof(str.c_str()), "%s", str.c_str());
  12. cout<<sz<<endl; // abcd
  13. cout<<sizeof(sz)<<endl; // 3
  14. cout<<strlen(sz)<<endl; // 4
  15. int ib = snprintf(sz, strlen(str.c_str()), "%s", str.c_str());
  16. cout<<sz<<endl; // abc
  17. cout<<ia<<endl; // 4
  18. cout<<ib<<endl; // 4
  19. return 0;
  20. }

看起来有点诡异。没错snprintf是可以复制超过sizeof(sz)个字符串的。
其实上面的snprintf的用法都不是一般用法!

常规用法

  1. char c[3];
  2. int ic = snprint(sz, sizeof(sz), "%s", "abcd");
  3. cout<<sz<<endl; // ab
  4. cout<<ic<<endl; // 4

这才是一般的用法。但是如果sz的长度太长,比如业务中设置的某个默认常量(MAX_LEN)通常都有1024甚至4096。完全拷贝MAX_LEN-1个字符,则是浪费!

返回值

snprintf的返回值也有trick。他返回的不是实际写入目标的字符个数(sprint是,snprint不是),而是预期要写入的字符个数,也就是不管参数二size十多少,返回值总是,format替换后的字符个数。

  1. char c[3];
  2. int ic = snprint(sz, sizeof(sz), "xyz%s", "abcd");
  3. cout<<sz<<endl; // xyz
  4. cout<<ic<<endl; // 7