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

标签 c c-strings c99

我经常希望有一种简单的方法来使用 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, ...));

我相信这是明确定义的,因为复合文字数组的生命周期将与封闭 block 相关联。有什么理由可以避免这种情况吗?

最佳答案

与在调用 snprintf_assert 之前简单定义本地数组相比,此技术存在一些缺点:

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

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

  • 可读性较差:这种技术并不常见,代码的临时读者会思考 local_sprintf 返回的指针的生命周期,尽管有明确的名称。

相反,以下是一些明显的优势:

  • 多功能性:可在任何表达式中使用的简单函数调用语法。

  • 简单:无需命名临时字符数组。

  • 可以通过本地定义的具有相同作用域和生命周期的指针多次使用。

    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 的额外调用来计算和传递,并通过另一个调用将其传递给 snprintf_assert,但应避免对宏参数进行多次求值并求值 snprintf 3 次至少可以说似乎不太优雅。

关于c - 使用复合文字数组作为临时字符串缓冲区是否合理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77359768/

相关文章:

c - 在 C99 中空白初始化结构数组

c - C中多数组的存储映射函数

c - Printf 在 Ubuntu 中不工作

c - 行中数组的类型

c - 如何用新行替换空格?

c - 如何添加两个字符串?

c - 如何编写类似 foreach 的宏来访问图形的所有节点

c - 当不支持重载函数时,C 中的 math.h 函数如何接受多种类型的参数?

将 unsigned char 转换为 char 会导致不同的二进制表示形式?

通过 memcpy 将数据从用户应用程序复制到内核驱动程序