提问人:rasmus 提问时间:10/25/2023 最后编辑:rasmus 更新时间:10/26/2023 访问量:67
使用复合文字数组作为临时字符串缓冲区是否合理?
Reasonable to use a compound literal array as a temporary string buffer?
问:
我经常希望我有一种简单的方法来使用 sprintf/snprintf 构造字符串,而无需定义本地数组,如下所示:
char str[256];
snprintf(str, sizeof(str), format, ...);
use_string(str);
我突然想到,我可以使用复合字面量来做到这一点:
static inline char* snprintf_assert(char* str, int max_size, const char* format, ...) {
va_list arg;
va_start(arg, format);
int count = vsnprintf(str, max_size, format, arg);
va_end(arg);
assert(count >= 0 && count < max_size);
return str;
}
// Helper sprintf that allocates a buffer local to the block.
// It's using the fact that (char[N]){} will allocate a l-value
// with lifetime tied to the local block.
#define local_sprintf(N, ...) snprintf_assert((char[N]){}, N, __VA_ARGS__)
用法:
use_string(local_sprintf(256, format, ...));
我相信这已经很好地定义了,因为 compund 文字数组的生命周期将绑定到封闭块。有什么理由避免这种情况吗?
答:
3赞
chqrlie
10/26/2023
#1
与在调用snprintf_assert
复合文本的可移植性较差:定义具有固定大小的数组(例如:)可移植到 C++ 和不支持 VLA 或复合文本的 C 实现,在块的开头定义数组当然是完全可移植的。
N
256
这种技术效率较低:您可以编写复合文本以向后移植到 C99,它定义但也初始化了临时的未命名数组,因此您会产生额外的字节数。这可能不是一个很大的开销,除非你做大以容纳潜在的大构造字符串。
(char[N]){}
(char[N]){0}
memset()
N
0
N
可读性较差:这种技术不是一个常见的习惯用语,你的代码的普通读者会思考 返回的指针的生存期,尽管有明确的名称。
local_sprintf
相反,这里有一些明显的优势:
多功能性:简单的函数调用语法可用于任何表达式。
简单性:无需命名临时 char 数组。
可以通过具有相同范围和生存期的本地定义的指针多次使用。
char *fullname = local_sprintf(256, "%s %s", first, last); register_user(fullname); output_user(fullname);
然而,对于这个用例来说,与经典代码相比,优势似乎微乎其微:
char fullname[156]; snprintf(fullname, sizeof fullname, "%s %s", first, last); register_user(fullname); output_user(fullname);
太糟糕了,数组长度仍然需要估计。当然,实际的数组大小可以通过对宏的额外调用来计算和传递,然后再调用一次将其传递给snprintf_assert,但应避免对宏参数进行多次计算,至少可以说,计算 3 次似乎很不优雅。snprintf
snprintf
评论
2赞
David C. Rankin
10/26/2023
@ShadowRanger那里的某个地方缺少一个浮动的“not”,因为它肯定不能移植到“不支持 VLA 或复合文字的 C 实现”......(同样适用于严格符合的C++)不像我以前从未说过一句话......:)
0赞
David C. Rankin
10/26/2023
但是,现在经过编辑,很明显固定大小 N 是有意为之的。这是 100% 有意义的,因为这将可移植到任何东西。夹头,有时意图并不总是很清楚......在法律、政治或编程方面。
0赞
chqrlie
10/26/2023
@ShadowRanger:我编辑了答案以澄清意图。
评论
vsnprintf()