C语言 snprintf()函数

介绍

snprintf 是 C 标准库(<stdio.h>)中安全的格式化字符串写入函数,核心是通过限制最大写入长度防止缓冲区溢出,是 sprintf 的安全替代。

函数原型

#include <stdio.h>
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。

示例代码

#include <stdio.h>
#include <string.h>

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

例子:

snprintf(buf, 10, "%05d", 12);
// 结果:00012

2. 保留小数位数

snprintf(buf, 20, "%.2f", 3.14159);
// 结果:3.14

3. 左右对齐

  • %-5d:左对齐(默认右对齐)

例子

#include <stdio.h>

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;
}