snprintf 学习笔记
一、核心定位
- 归属:C 语言标准库函数(头文件 #include <stdio.h>)
- 功能:将格式化字符串写入字符缓冲区(char 数组)
- 核心优势:自带缓冲区越界保护,是 sprintf 的安全升级版(工程开发首选)
二、函数原型与参数
1. 标准原型
cpp
int snprintf(char *str, size_t size, const char *format, ...);
2. 参数详解
|--------|--------------|-----------------|---------------------------------|
| 参数 | 类型 | 作用 | 关键注意点 |
| str | char* | 目标缓冲区(存储结果) | 需指向有效可写内存(预计算时可设为 NULL) |
| size | size_t | 缓冲区总字节数 | 必须包含末尾 \0 的占位符(有效字符数 = size-1) |
| format | const char* | 格式控制串(同 printf) | 支持 %s/%d/%f/%.2lf 等占位符 |
| ... | 可变参数 | 待格式化的数据 | 需与 format 占位符一一对应 |
三、核心特性(安全关键)
1. 越界保护机制
- 当格式化后字符串长度 ≥ size 时:仅写入 size - 1 个有效字符,末尾强制补 \0
- 彻底避免缓冲区溢出(sprintf 无此特性,易导致程序崩溃)
2. 返回值规则(必记)
- 返回值 = 格式化后应生成的字符串总长度(不含 \0 )(与缓冲区是否装下无关)
- 判定逻辑:
- 返回值 < size → 无截断,完整写入
- 返回值 ≥ size → 有截断,仅写入 size - 1 个字符
四、基础用法(分场景)
场景 1:无截断(正常场景)
cpp
char buf[20]; // 缓冲区大小20字节
int ret = snprintf(buf, sizeof(buf), "姓名:%s,年龄:%d", "张三", 20);
// 输出:buf = "姓名:张三,年龄:20",ret = 14(不含\0)
场景 2:有截断(缓冲区不足)
cpp
char buf[12]; // 缓冲区仅12字节
int ret = snprintf(buf, sizeof(buf), "姓名:%s,年龄:%d", "张三", 20);
// 输出:buf = "姓名:张三,年"(11个有效字符),ret = 14,buf[11] = '\0'
场景 3:高级用法(动态分配缓冲区)
核心思路:预计算长度 → 动态分配 → 格式化写入
cpp
// 1. 预计算所需长度(str=NULL,size=0,不写入仅返回长度)
int need_len = snprintf(NULL, 0, "ID:%d,战力:%ld", 1001, 123456);
// 2. 动态分配缓冲区(+1 留\0位置)
char *buf = (char*)malloc(need_len + 1);
// 3. 正式写入
snprintf(buf, need_len + 1, "ID:%d,战力:%ld", 1001, 123456);
// 4. 用完释放内存
free(buf);
buf = NULL;
优势:无截断、不浪费内存
五、与 sprintf 的核心区别
|---------|----------------|-----------|
| 特性 | snprintf | sprintf |
| 缓冲区溢出保护 | ✅ 自动截断 + 补 \0 | ❌ 无,直接越界 |
| 安全性 | 高(工程首选) | 极低(不推荐使用) |
| 参数要求 | 必须传缓冲区大小 size | 无需传大小 |
| 缓冲区不足行为 | 截断并补 \0 | 覆盖后续内存 |
六、避坑指南(重点)
- size 必须传缓冲区总字节数:用 sizeof(buf) 而非 strlen(buf)(strlen 不含 \0)
- 返回值不含 \0:判断截断的条件是 ret ≥ size,而非 ret > size
- 占位符与参数类型匹配 :
- %d → int,%ld → long,%lld → long long
- %f → float,%lf → double,%s → char*
- 缓冲区非空(除非预计算):仅预计算长度时可设 str = NULL,其他场景需指向有效内存
- 动态分配必做校验:malloc 后需判断 buf != NULL,避免内存分配失败
七、核心要点速记
- 安全核心:截断 + 补 \0,杜绝溢出
- 关键参数:size 是总字节数(含 \0)
- 返回值:应生成的长度(不含 \0),ret≥size 即截断
- 工程原则:优先用 snprintf 替代 sprintf
- 高级技巧:str = NULL + size = 0 预计算长度,配合 malloc 动态分配
八、实际用例
将九九乘法表保存进数组中,并且从数组中输出,只能用while循环。
cpp
//1*1=1
//1*2=2 2*2=4
//1*3=3 2*3=6 3*3=9
//1*4=4 2*4=8 3*4=12 4*4=16
//1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
//1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
//1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
//1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
//1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
#include<stdio.h>
int main()
{
char shuzu[9][70];
int i = 1;
while(i <= 9){
shuzu[i - 1][0] = '\0';
int j = 1;
while(j <= i){
printf("%d*%d=%d\t", i, j, i*j);
snprintf(shuzu[i - 1], sizeof(shuzu[i - 1]),"%s%d*%d=%d ", shuzu[i - 1], j, i, i*j);
j++;
}
printf("\n");
i++;
}
printf("\n从数组中打印乘法表:\n\n");
int hang = 0;
while(hang <= 9){
printf("%s \n", shuzu[hang]);
hang++;
}
return 0;
}
运行结果:
