C-使用VA_LIST和VA_ARG实现子功能



我有一个我想将其拆分为va_list式子功能的varargs式函数。原始功能:

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_listva_arg()的设置或使用中的某些内容?

除了由@zwol识别出的良好确定...

container_append(c, one or more arguments, NULL);是电势未定义的行为(ub(。

container_append()期望element_t *elementNULL可能只是0

NULL扩展为实现定义的空指针常数...

具有值0的整数常数表达式,或这样的表达式铸型 void *,称为 null指针常数

NULL甚至没有指定与指针相同的大小。

va_arg(ap, element_t *)是ub。

要进行更安全的电话,请使用container_append(c, args, (element_t *) NULL);

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_start(ap, element)(在呼叫者中(将设置VA_ARG以首先返回元素。也许它不起作用?

的确,它不能那样起作用。va_start设置va_arg以返回匿名参数的第一个。如果没有任何匿名论点,那么您第一次致电va_arg并触发UB。

相关内容

  • 没有找到相关文章

最新更新