使用连接和格式文字动态分配“char*”?

Dynamically allocate `char*` with concatention and format literals?

在支持 macOS、Windows (MSVC) 和 Linux 的情况下,我该如何执行以下操作?

char *s;
func(&s, "foo");
if (<condition>) func(&s, "bar%s", "can")
/* want "foobarcan", and I don't know `strlen(s)` AoT */

我试过 asprintf (was able to find an MSVC implementation) but that didn't seem to work well on this kind of workflow. fopencookie and funopen 看起来很方便,但在 MSVC 上不可用。

也许 realloc 有一些干净的方法可以在 C 中创建以 char* 结尾的 NUL?

正如评论中指出的那样,(v)snprintf 始终 returns 写入的字节数(不包括空终止字节),即使被截断。这具有为函数提供 0 returns to-be-formatted 字符串长度的 size 参数的效果。

使用这个值,加上我们现有字符串的字符串长度(如果适用),再加一,我们(重新)分配适当的内存量。

要连接,只需在正确的偏移处打印格式化字符串即可。

一个例子,没有错误检查。

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *dstr(char **unto, const char *fmt, ...) {
    va_list args;
    size_t base_length = unto && *unto ? strlen(*unto) : 0;

    va_start(args, fmt);
                /* check length for failure */
    int length = vsnprintf(NULL, 0, fmt, args);
    va_end(args);

                /* check result for failure */
    char *result = realloc(unto ? *unto : NULL, base_length + length + 1);

    va_start(args, fmt);
                /* check for failure*/
    vsprintf(result + base_length, fmt, args);
    va_end(args);

    if (unto)
        *unto = result;

    return result;
}

int main(void) {
    char *s = dstr(NULL, "foo");

    dstr(&s, "bar%s%d", "can", 7);

    printf("[[%s]]\n", s);

    free(s);
}

stdout:

[[foobarcan7]]

这里的警告是你不能写:

char *s;
dstr(&s, "foo");

s 必须初始化为 NULL,或者该函数必须直接用作初始化器,第一个参数设置为 NULL.

那个,第二个参数总是被视为格式字符串。如果第一个字符串包含不卫生的数据,请使用其他方式预分配第一个字符串。

利用示例:

/* exploit */
char buf[128];
fgets(buf, sizeof buf, stdin);

char *str = dstr(NULL, buf);
puts(str);
free(str);

stdin:

%d%s%s%s%s%d%p%dpapdpasd%d%.2f%p%d

结果:Undefined Behavior