C语言 snprintf()函数 作者:马育民 • 2026-03-31 13:36 • 阅读:10000 # 介绍 `snprintf` 是 C 标准库(``)中**安全的格式化字符串写入函数**,核心是通过限制最大写入长度防止缓冲区溢出,是 `sprintf` 的安全替代。 # 函数原型 ```c #include int snprintf(char *restrict str, size_t size, const char *restrict format, ...); ``` ##### 参数说明 - **str**:目标字符缓冲区,用于存放格式化后的字符串。 - **size**:缓冲区总大小(**包含末尾 `\0` 终止符**)。函数最多写入 `size-1` 个有效字符,最后自动补 `\0`。 - **format**:格式化字符串(与 `printf` 一致,如 `%d`、`%s`、`%f` 等)。 - **...**:可变参数列表,对应 `format` 中的占位符。 ##### 返回值 - **成功**:返回**本该写入的字符总数(不含末尾 `\0`)**,无论是否被截断。 - **失败**:返回**负数**(如格式化错误、编码异常)。 # 使用说明 - 当格式化后内容长度 < `size`:完整写入,末尾补 `\0`。 - 当格式化后内容长度 ≥ `size`:**自动截断**,只写入前 `size-1` 个字符,末尾强制补 `\0`,保证字符串合法。 - 若 `size = 0`:不写入任何内容,`str` 可为 `NULL`。 ### 示例代码 ```c #include #include int main() { char buf[10]; // 缓冲区大小 10 int ret; // 场景1:内容未超出缓冲区 ret = snprintf(buf, sizeof(buf), "num: %d", 123); printf("buf: %s | ret: %d | strlen: %zu\n", buf, ret, strlen(buf)); // 输出:buf: num: 123 | ret: 6 | strlen: 6 // 场景2:内容超出缓冲区(自动截断) ret = snprintf(buf, sizeof(buf), "long: %s", "abcdefghijklmn"); printf("buf: %s | ret: %d | strlen: %zu\n", buf, ret, strlen(buf)); // 输出:buf: long: abc | ret: 15 | strlen: 9 // 场景3:根据返回值动态分配足够空间 const char *long_str = "this is a very long string"; ret = snprintf(NULL, 0, "%s", long_str); // 先获取所需长度 if (ret >= 0) { char *dyn_buf = malloc(ret + 1); if (dyn_buf) { snprintf(dyn_buf, ret + 1, "%s", long_str); printf("dyn_buf: %s\n", dyn_buf); free(dyn_buf); } } return 0; } ``` # 与 sprintf 的关键区别 | 特性 | `snprintf` | `sprintf` | |:--- |:--- |:--- | | **安全性** | 安全,限制长度,防溢出 | 不安全,无长度检查,易溢出 | | **返回值** | 本该写入的字符数(不含 `\0`) | 实际写入的字符数(不含 `\0`) | | **截断** | 自动截断并补 `\0` | 不截断,直接溢出 | | **标准** | C99 标准 | C89/C99 均支持 | # 注意 1. **固定缓冲区安全写入**:优先用 `snprintf` 替代 `sprintf`,传入 `sizeof(buf)` 作为 `size`。 2. **动态分配足够空间**:先用 `snprintf(NULL, 0, ...)` 获取所需长度,再 `malloc` 分配,避免截断。 3. **判断是否截断**:若返回值 `ret >= (int)size`,说明内容被截断。 4. **避免重叠内存**:`str` 与源字符串/参数内存重叠时,行为未定义。 # % 格式说明符 `%` 是**格式化占位符**,用来告诉 `snprintf` 把变量按什么格式写入字符串。我直接给你**最常用、最实用、开发必背**的全套说明符,配例子,一看就会。 ### 1. 整数类型 | 说明符 | 作用 | 示例 | |--------|------|------| | `%d` / `%i` | 十进制 **int** 整数 | `snprintf(s, len, "%d", 123);` → `"123"` | | `%u` | 无符号十进制整数 | `%u` → 只显示正数 | | `%x` | 小写十六进制 | `%x` → `1a` | | `%X` | 大写十六进制 | `%X` → `1A` | | `%o` | 八进制 | `%o` → `32` | ### 2. 长整数 / 64位整数 | 说明符 | 作用 | |--------|------| | `%ld` | long 整数 | | `%lld` | long long 整数(64位) | | `%lu` | unsigned long | | `%llu` | unsigned long long | ### 3. 字符与字符串 | 说明符 | 作用 | 示例 | |--------|------|------| | `%c` | 单个字符 | `%c` → `'A'` | | `%s` | 字符串 | `%s` → `"hello"` | ### 4. 浮点数 | 说明符 | 作用 | |--------|------| | `%f` | float / double 浮点数 | | `%.2f` | 保留 **2位小数**(最常用) | | `%e` | 科学计数法(小写) | | `%E` | 科学计数法(大写) | ### 5. 指针地址 | 说明符 | 作用 | |--------|------| | `%p` | 打印变量/指针的内存地址 | --- ## 高级格式控制 ### 1. 固定宽度、补零 - `%5d`:占 **5个字符宽度**,不足补空格 - `%05d`:占 **5个字符宽度**,不足补 **0** 例子: ```c snprintf(buf, 10, "%05d", 12); // 结果:00012 ``` ### 2. 保留小数位数 ```c snprintf(buf, 20, "%.2f", 3.14159); // 结果:3.14 ``` ### 3. 左右对齐 - `%-5d`:**左对齐**(默认右对齐) --- ## 例子 ```c #include int main() { char buf[100]; // 整数 snprintf(buf, sizeof(buf), "整数:%d", 100); printf("%s\n", buf); // 整数:100 // 十六进制 snprintf(buf, sizeof(buf), "十六进制:%X", 26); printf("%s\n", buf); // 十六进制:1A // 字符串 + 字符 snprintf(buf, sizeof(buf), "姓名:%s,等级:%c", "小明", 'A'); printf("%s\n", buf); // 姓名:小明,等级:A // 浮点数(保留2位) snprintf(buf, sizeof(buf), "价格:%.2f", 19.99); printf("%s\n", buf); // 价格:19.99 // 固定宽度补0 snprintf(buf, sizeof(buf), "编号:%04d", 8); printf("%s\n", buf); // 编号:0008 return 0; } ``` 原文出处:http://www.malaoshi.top/show_1GW32tWkm7nM.html