使用 char[] 获取可变大小的 C 可变参数
Obtaining C varargs with variable size using char[]
我正在 C
中编写一个容器库,我想在我的实现中使用可变参数,如下所示:
void stack_push(stack *self, T item);
T stack_pop(stack *self);
显然,C 没有泛型类型,所以我使用 void *
指针:
void stack_push(stack *self, void *item);
void *stack_pop(stack *self);
但是,我正在考虑使用可变参数传递输入:
void stack_push(stack *self, ...);
这可行,因为项目大小是在容器初始化时确定的。
我的问题是:使用具有相同大小的不同类型访问可变参数成员是否有效C
?
void stack_push(stack *self, ...)
{
struct wrapper {
char item[self->item_size];
};
va_list ap;
struct wrapper item;
va_start(ap, self);
item = va_arg(ap, struct wrapper);
va_end(ap);
stack_grow(self, self->size+1);
memcpy(self->items+self->item_size*self->size++, &item, self->item_size);
}
按您的意愿行事会引发未定义的行为。
来自 C 标准(C11 草案):
7.16.1.1 The va_arg macro
Synopsis
1
#include <stdarg.h>
type va_arg(va_list ap, type);
Description
2 [...] if
type is not compatible with the type of the actual next argument (as promoted according
to the default argument promotions), the behavior is undefined, except for the following
cases:
— one type is a signed integer type, the other type is the corresponding unsigned integer
type, and the value is representable in both types;
— one type is pointer to void and the other is a pointer to a character type.
None 两个异常似乎符合您的用例。
进一步回答我自己的问题:不,不可能将可变参数作为 char[]
与 sizeof type
:
type
in va_arg(ap, type);
不能是数组。
type
可以是 struct
,但 struct
不能有 VLA。
- 对于 GCC,它期望 VLA
struct
s 作为指针传递。
- 多次调用
va_arg(ap, char);
会导致未定义的行为。 va_arg 调用的数量必须等于参数的数量。
您能做的最好的事情就是为每个可能的项目大小定义一个类型,然后使用它加上一个 switch 语句。
struct s1 { char x[1]; };
struct s2 { char x[2]; };
...
void stack_push(stack *s, ...)
{
va_list ap;
union {
struct s1 s1;
struct s2 s2;
...
} u;
va_start(ap, s);
switch (s->item_size)
{
case 1:
u.s1 = va_arg(ap, struct s1);
break;
case 2:
u.s2 = va_arg(ap, struct s2);
break;
...
}
va_end(s);
stack_grow(s, self->size+1);
memcpy(self->items + self->item_size*self->size++, &u, self->item_size);
}
但是,对于任何试图实现类似机制(即传递文字而不是指针)的人,我建议如下:
#include <stack>
#define stack(T) struct {stack actual; T item;}
#define stack_init(s) (stack_init)(&(s)->actual, sizeof((s)->item))
#define stack_push(s, i) ((s)->item = (i); (stack_push)(&(s)->actual, &(s)->item))
#define stack_pop(s) (memset(&(s)->item, 0, sizeof((s)->item)), (stack_pop)(&(s)->actual, &(s)->item), (s)->item)
int main(void)
{
stack(int) s;
stack_init(&s);
stack_push(&s, 3);
printf("%d\n", stack_pop(&s)); // Prints 3
printf("%d\n", stack_pop(&s)); // Prints 0
stack_fini(&s);
return 0;
}
我正在 C
中编写一个容器库,我想在我的实现中使用可变参数,如下所示:
void stack_push(stack *self, T item);
T stack_pop(stack *self);
显然,C 没有泛型类型,所以我使用 void *
指针:
void stack_push(stack *self, void *item);
void *stack_pop(stack *self);
但是,我正在考虑使用可变参数传递输入:
void stack_push(stack *self, ...);
这可行,因为项目大小是在容器初始化时确定的。
我的问题是:使用具有相同大小的不同类型访问可变参数成员是否有效C
?
void stack_push(stack *self, ...)
{
struct wrapper {
char item[self->item_size];
};
va_list ap;
struct wrapper item;
va_start(ap, self);
item = va_arg(ap, struct wrapper);
va_end(ap);
stack_grow(self, self->size+1);
memcpy(self->items+self->item_size*self->size++, &item, self->item_size);
}
按您的意愿行事会引发未定义的行为。
来自 C 标准(C11 草案):
7.16.1.1 The va_arg macro
Synopsis
1
#include <stdarg.h> type va_arg(va_list ap, type);
Description
2 [...] if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:
— one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;
— one type is pointer to void and the other is a pointer to a character type.
None 两个异常似乎符合您的用例。
进一步回答我自己的问题:不,不可能将可变参数作为 char[]
与 sizeof type
:
type
inva_arg(ap, type);
不能是数组。type
可以是struct
,但struct
不能有 VLA。- 对于 GCC,它期望 VLA
struct
s 作为指针传递。 - 多次调用
va_arg(ap, char);
会导致未定义的行为。 va_arg 调用的数量必须等于参数的数量。
您能做的最好的事情就是为每个可能的项目大小定义一个类型,然后使用它加上一个 switch 语句。
struct s1 { char x[1]; };
struct s2 { char x[2]; };
...
void stack_push(stack *s, ...)
{
va_list ap;
union {
struct s1 s1;
struct s2 s2;
...
} u;
va_start(ap, s);
switch (s->item_size)
{
case 1:
u.s1 = va_arg(ap, struct s1);
break;
case 2:
u.s2 = va_arg(ap, struct s2);
break;
...
}
va_end(s);
stack_grow(s, self->size+1);
memcpy(self->items + self->item_size*self->size++, &u, self->item_size);
}
但是,对于任何试图实现类似机制(即传递文字而不是指针)的人,我建议如下:
#include <stack>
#define stack(T) struct {stack actual; T item;}
#define stack_init(s) (stack_init)(&(s)->actual, sizeof((s)->item))
#define stack_push(s, i) ((s)->item = (i); (stack_push)(&(s)->actual, &(s)->item))
#define stack_pop(s) (memset(&(s)->item, 0, sizeof((s)->item)), (stack_pop)(&(s)->actual, &(s)->item), (s)->item)
int main(void)
{
stack(int) s;
stack_init(&s);
stack_push(&s, 3);
printf("%d\n", stack_pop(&s)); // Prints 3
printf("%d\n", stack_pop(&s)); // Prints 0
stack_fini(&s);
return 0;
}