如何为其自己的数据结构使用可变参数?

How to use a variadic argument for its own data structure?

为了方便使用包含要从函数填充的字符串的数据结构,我希望能够使用可变参数定义相同的函数,如下所示:

struct my_struct_t
{
    char *msg;
};

struct my_struct_t *fill(const char *fmt, ...);

struct my_struct_t *filled = fill("A number: %d, a string: '%s'.", 43, "hello");

为此,我实现了以下功能,以及变体,但是当我在结构中检索字符串时,结果总是错误的。这是代码:

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

struct my_struct_t
{
    char *msg;
};

struct my_struct_t *fill(const char *fmt, ...)
{
    va_list ap1, ap2;
    va_copy(ap2, ap1);
    va_start(ap1, fmt);

    int slen = snprintf(NULL, 0, fmt, ap1);
    va_end(ap1);

    char *str = malloc(slen);
    assert(str != NULL);

    va_start(ap2, fmt);
    snprintf(str, slen, fmt, ap2);
    va_end(ap2);

    struct my_struct_t *my_struct = malloc(sizeof *my_struct);
    assert(my_struct != NULL);

    my_struct->msg = str;

    return my_struct;
}

int main()
{
    struct my_struct_t *filled = fill("A number: %d, a string: '%s'.", 43, "hello");
    printf("%s\n", my_struct->msg);
    return 0;
}

这会导致每次执行的结果不同,例如:

A number: 7011816, a string: 'ðe'

我想这是使用可变参数的问题,但是我还没有找到解决问题的方法,即将发送的格式保存在结构字段中的字符串,所以我希望这样:

A number: 43, a string: 'hello'.

快速修复。代码中的注释

struct my_struct_t *fill(const char *fmt, ...) {
    va_list ap1, ap2;

    // change order 
    va_start(ap1, fmt);
    va_copy(ap2, ap1);

    // Use vsnprintf
    //int slen = snprintf(NULL, 0, fmt, ap1);
    int slen = vsnprintf(NULL, 0, fmt, ap1);
    va_end(ap1);

    // test result
    assert(slen >= 0);
    // ... or a pedantic test
    assert(slen >= 0 && (unsigned) slen < SIZE_MAX);

    // Need + 1 for null character
    // char *str = malloc(slen); 
    char *str = malloc(slen + 1u);
    assert(str != NULL);

    // No va_start, copy is enough
    // va_start(ap2, fmt);

    // snprintf(str, slen, fmt, ap2);
    // Since we a going for broke, no need for `n`, pedantically we could/should use vsnprintf()
    // vsnprintf(str, slen+1u, fmt, ap2);
    vsprintf(str, fmt, ap2);
    va_end(ap2);

    // Good use of sizing by referenced type
    struct my_struct_t *my_struct = malloc(sizeof *my_struct);
    assert(my_struct != NULL);

    my_struct->msg = str;

    return my_struct;
}

而不是 assert(),代码可以 return NULL。一定要释放资源。

struct my_struct_t *fill(const char *fmt, ...) {
    va_list ap1, ap2;
    va_start(ap1, fmt);
    va_copy(ap2, ap1);

    int slen = vsnprintf(NULL, 0, fmt, ap1);
    va_end(ap1);
    if (slen < 0 || (unsigned) slen >= SIZE_MAX) {
      va_end(ap2);
      return NULL;
    }

    char *str = malloc(slen + 1u);
    if (str == NULL) {
      va_end(ap2);
      return NULL;
    }

    slen = vsnprintf(str, slen+1u, fmt, ap2);
    va_end(ap2);
    if (slen < 0 || (unsigned) slen >= SIZE_MAX) {
      free(str); 
      return NULL;
    }

    struct my_struct_t *my_struct = malloc(sizeof *my_struct);
    if (my_struct == NULL) {
      free(str); 
      return NULL;
    }
    my_struct->msg = str;
    return my_struct;
}