使用复合文字数组作为临时字符串缓冲区是否合理?

Reasonable to use a compound literal array as a temporary string buffer?

提问人:rasmus 提问时间:10/25/2023 最后编辑:rasmus 更新时间:10/26/2023 访问量:67

问:

我经常希望我有一种简单的方法来使用 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 文字数组的生命周期将绑定到封闭块。有什么理由避免这种情况吗?

C C弦 C99

评论

0赞 the busybee 10/26/2023
您是否知道在发生错误时返回值为负数?为什么不使用 VLA?vsnprintf()
0赞 rasmus 10/26/2023
@thebusybee 我已经编辑了代码以处理负值。请在这里解释 VLA 如何提供帮助。

答:

3赞 chqrlie 10/26/2023 #1

与在调用snprintf_assert

  • 复合文本的可移植性较差:定义具有固定大小的数组(例如:)可移植到 C++ 和不支持 VLA 或复合文本的 C 实现,在块的开头定义数组当然是完全可移植的。N256

  • 这种技术效率较低:您可以编写复合文本以向后移植到 C99,它定义但也初始化了临时的未命名数组,因此您会产生额外的字节数。这可能不是一个很大的开销,除非你做大以容纳潜在的大构造字符串。(char[N]){}(char[N]){0}memset()N0N

  • 可读性较差:这种技术不是一个常见的习惯用语,你的代码的普通读者会思考 返回的指针的生存期,尽管有明确的名称。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 次似乎很不优雅。snprintfsnprintf

评论

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:我编辑了答案以澄清意图。