如何为自己的数据结构使用一个变量参数?

问题描述 投票:0回答:1

为了方便使用一个包含字符串的数据结构从一个函数中填充,我希望能够像这样用变量参数定义同一个函数。

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;
}

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

一个数字:7011816,一个字符串:"ðe

我猜这是使用变量参数的问题,然而我还没有找到如何解决我的问题,即在结构字段中保存字符串与发送的格式,所以我期望这样。

一个数字: 43,一个字符串:"你好"。

c variadic-functions
1个回答
1
投票

一个快速可能的修复方法。 代码中的注释

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(),代码可以返回 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;
}
© www.soinside.com 2019 - 2024. All rights reserved.