我正在C
编写一个容器库,我想在我的实现中使用varargs,如下所示:
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);
但是,我正在考虑使用varargs传递输入:
void stack_push(stack *self, ...);
这可行,因为项目大小是在容器初始化时确定的。
我的问题是:使用相同大小的不同类型访问vararg成员是否有效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);
}
答案 0 :(得分:2)
按照您的意图行事,引发未定义的行为。
来自C标准(C11草案):
7.16.1.1 va_arg宏
<强>概要强>
1
#include <stdarg.h> type va_arg(va_list ap, type);
<强>描述强>
2 [...]如果 type 与实际下一个参数的类型不兼容(根据 对于默认参数提升),行为是未定义的,除了以下内容 例:
- 一种类型是有符号整数类型,另一种类型是相应的无符号整数 类型,值可以在两种类型中表示;
- 一种类型是指向void的指针,另一种是指向字符类型的指针。
这两个例外似乎都不符合您的用例。
答案 1 :(得分:1)
要进一步回答我自己的问题:不,不可能以char[]
sizeof type
获取varargs:
type
中的va_arg(ap, type);
不能是数组。type
可以是struct
,但struct
不能拥有VLA。struct
作为指针传递。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);
}
但是,对于任何试图实现similair机制的人(即传递文字而不是指针),我建议如下:
#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;
}