代码之家  ›  专栏  ›  技术社区  ›  Some Name

用于组成格式化字符串的snprintf用法

  •  3
  • Some Name  · 技术社区  · 7 年前

    我想知道 snprintf 发现 this answer 举例来说:

    char buf[20] = "";
    char *cur = buf, * const end = buf + sizeof buf;
    cur += snprintf(cur, end-cur, "%s", "foo");
    printf("%s\n", buf);
    if (cur < end) {
        cur += snprintf(cur, end-cur, "%s", " bar");
    }
    printf("%s\n", buf);
    free(str);
    

    我不清楚的是,我们分配了一个固定的硬编码缓冲区大小,这个大小似乎受到缓冲区溢出的影响。在N1570中,我发现(7.21.6.5)

    #include <stdio.h>
    int snprintf(char * restrict s, size_t n,
    const char * restrict format, ...);
    

    2.snprintf函数等于fprintf,除了 输出被写入一个数组(由参数s指定),而不是 流向溪流如果n为零,则不写入任何内容,s可能为空。 指针。

    所以在我看来,习惯用法如下:

    int need_space = snprintf(NULL, 0, "abs %s", "fgh") + 1; //How much to allocate?
    char *const str = malloc(need_space * sizeof(char)); //allocate
    int written = snprintf(str, need_space, "abs %s", "fgh"); //do format
    printf("Need space = %d, written = %d\n", need_space, written);
    printf("%s\n", str);
    

    或者这不是常见的问题?

    1 回复  |  直到 7 年前
        1
  •  2
  •   chux    7 年前

    2x调用 snprintf() 是一个常见的成语。

    …还有别的问题吗?

    问题就在角落里

    格式维护

    下面是一个重复的格式——随着代码的老化,很容易被破坏,只有一行被更改。

    // Oops!
    int need_space = snprintf(NULL, 0, "abs %s", "fgh") + 1;
    char *const str = malloc(need_space * sizeof(char));
    int written = snprintf(str, need_space, "abs %s ", "fgh");
    

    你注意到区别了吗?

    最好只说明一次格式。

    #define FMT_ABS_S "abs %s"
    int need_space = snprintf(NULL, 0, FMT_ABS_S, "fgh");
    char *const str = malloc(sizeof *str * (need_space + 1u));
    int written = snprintf(str, need_space, FMT_ABS_S, "fgh");
    

    波动

    代码需要确保值不会更改(非- volatile )在两次调用之间,多线程应用程序中会出现特殊问题。

    错误检查

    代码缺少检查。然而,即使有了支票,如何处理意想不到的结果——也许只是保释?

    static const char *fmt_s = "abs %s";
    int need_space = snprintf(NULL, 0, fmt_s, "fgh");
    if (need_space < 0) {
      Handle_EncodingError();
    }
    char *const str = malloc(sizeof *str * (need_space + 1u));
    if (str == NULL) {
      Handle_OutOfMemory();
    }
    int written = snprintf(str, need_space, fmt_s, "fgh");
    if (written < 0 || written > need_space) {
      Handle_Error();
    }
    

    不过要去两次

    在某些情况下,2个调用可能是浪费的,但通常影响比想象的要小。 @Jonathan Leffler

    然而对我来说,“如果分配失败该怎么办”无论如何都是一个显示障碍,代码报告错误并可能退出。

    有选择地,一次就足够了

    s*printf() 格式是严格控制的,我认为一个调用就足够了。

    // int to string
    
    #define LOG2_N 28
    #define LOG2_D 93
    // Number of char needed for a string of INT_MIN is log10(bit width) + 3
    #define INT_LEN ((sizeof(int)*CHAR_BIT-1)*LOG2_N/LOG2_D + 3)
    
    char s[INT_LEN * 2];  // I like to use 2x to handle locale issues, no need to be stingy here.
    
    int len = snprintf(s, sizeof s, "%d", i);
    
    if (len < 0 || len >= sizeof s) {
      Handle_VeryUnusualFailure();
    }