我有一个varargs风格的函数,我想把它拆分成一个va_list风格的子函数原始功能:
void container_append(container_t *c, element_t *element, ...) {
element_t *e;
va_list ap;
va_start(ap, element);
while((e = va_arg(ap, element_t *)) != NULL) {
container_append_aux(c, e);
}
va_end(ap);
}
请注意,调用者必须用NULL终止元素列表,但这不会导致任何问题重构:
void container_append(container_t *c, element_t *element, ...) {
va_list ap;
va_start(ap, element);
container_vappend(c, ap);
va_end(ap);
}
void container_vappend(container_t *c, va_list ap) {
element_t *e;
while ((e = va_arg(ap, element_t *)) != NULL) {
container_append_aux(c, e);
}
}
然而,当我这样称呼它时:
container_append(c, NULL);
... 在
container_vappend()
内部,对va_arg()
的调用返回了不为空的内容。这是一个更复杂的函数的转录,但是除了任何打字错误,我是否在
va_list
和va_arg()
的设置或使用中遗漏了什么? 最佳答案
当这样调用container_append
时
container_append(c, NULL);
命名参数
element
将为0,并且不会有任何匿名参数在这些条件下,container_append
根本不能调用va_arg
,否则程序具有未定义的行为。在重构代码之前,它碰巧是意外工作的,但是原始代码和重构版本一样有缺陷。您可以在循环之前检查
element
。。。void
container_append(container_t *c, element_t *element, ...)
{
if (!element) return;
container_append_aux(c, element);
va_list ap;
va_start(ap, element);
while ((element = va_arg(ap, element_t *)))
container_append_aux(c, element);
va_end(ap);
}
... 或者可以将所有元素参数设为匿名:
void
container_append(container_t *c, ...)
{
va_list ap;
va_start(ap, c);
element_t *e;
while ((e = va_arg(ap, element_t *)))
container_append_aux(c, e);
va_end(ap);
}
后一种结构与您要执行的重构更兼容。
编辑:关于注释中的此查询:
我以为
vappend
(在调用者中)会设置va_arg以首先返回元素也许这样不行?事实上,它不是那样工作的。
va_start(ap, element)
设置va_start
以返回第一个匿名参数如果没有任何匿名参数,那么您在第一次调用va_arg
并触发UB时就离开了末尾。关于c - 使用va_list和va_arg实现子功能,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56811604/