如何为其自己的数据结构使用可变参数?
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;
}
为了方便使用包含要从函数填充的字符串的数据结构,我希望能够使用可变参数定义相同的函数,如下所示:
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;
}