为了方便使用一个包含字符串的数据结构从一个函数中填充,我希望能够像这样用变量参数定义同一个函数。
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,一个字符串:"你好"。
一个快速可能的修复方法。 代码中的注释
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;
}