我正在尝试了解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);
或者这不常见并且存在另一个问题?
答案 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();
}