我经常希望我有一种简单的方法来构造一个带有Sprintf/Snprint tf的字符串,而不需要定义一个本地数组,如下所示:

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, ...));

我相信这是很好的定义,因为复合文字数组的生存期将与封闭的块绑定在一起.有什么理由可以避免这一点吗?

推荐答案

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

  • 将数组定义为固定大小N可以移植到C++和不支持VLA或复合文字的C实现.

  • 复合文字(char[N]){}不仅定义了临时未命名数组,而且还定义了initializes临时未命名数组,因此产生了N个字节到0个字节的额外memset().这可能不是很大的开销,除非您将N设置得很大以容纳可能很大的构造字符串.

一些明确的优势:

  • 可在任何表达式中使用的简单函数调用语法.

  • 不需要命名临时字符array.

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

    char *fullname = local_sprintf(256, "%s %s", first, last);
    register_user(fullname);
    output_user(fullname);
    

    然而,对于这个用例来说,相对于classic 代码的优势似乎微不足道:

    char fullname[156];
    snprintf(fullname, sizeof fullname, "%s %s", first, last);
    register_user(fullname);
    output_user(fullname);
    

太糟糕了,仍然需要估计数组长度.当然,实际的数组大小可以通过宏中对snprintf的两次模式调用来计算和传递,但应该避免对宏参数进行多次求值,至少可以说,对snprintf求值3次似乎不太优雅.

C++相关问答推荐

位屏蔽对于无符号转换是强制的吗?

为什么在传输 Big Data 时共享内存段的运行时间比管道更长?

ARM64 ASIMD固有的加载uint8_t* 到uint16x8(x3)?

有没有可能我不能打印?(C,流程)

Ruby C Api处理异常

致命:ThreadSaniizer:在Linux内核6.6+上运行时意外的内存映射

在Linux上使用vscode和lldb调试用Makefile编译的c代码

是否需要包括<;errno.h>;才能使用perror?

ifdef __cplusplus中的整数文字单引号

关于";*(++p)->;t";、&++p->;t";和&q;++*p->;t";的问题

Boyer Moore算法的简单版本中的未定义行为

处理EPOLL_WAIT中的接收数据和连接关闭信号

在C中包装两个数组?

正数之和是负数

仅从限制指针参数声明推断非混叠

Printf()在C中打印终止字符之后的字符,我该如何解决这个问题?

如何在Rust中处理C的longjmp情况?

WSASocket在哪里定义?

程序打印一些随机空行

我该如何处理这个 C 90 代码中的内存泄漏?