snprintf用于组成格式化字符串

时间:2019-01-16 07:02:46

标签: c printf

我正在尝试了解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)

  

1

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

2 snprintf函数与fprintf等效,除了   输出写入数组(由参数s指定),而不是写入   流。如果n为零,则不写任何内容,并且s可以为null   指针。

在我看来,它的惯用用法如下:

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 个答案:

答案 0 :(得分:2)

snprintf()的2x调用是常见的习惯用法。

  

...还有其他问题吗?

问题就在角落

格式维护

下面的代码格式重复-容易随着代码的老化而中断,并且只更改了一行。

// 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()格式后,我认为1个电话就足够了。

// 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();
}