简体   繁体   English

使用va_list和va_arg实现子功能

[英]Implementing a sub-function with va_list and va_arg

I have a varargs-style function that I want to split out to a va_list-style sub-function. 我有一个varargs样式的函数,我想拆分为va_list样式的子函数。 The original function: 原始功能:

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);
}

Note that the caller must terminate the list of elements with a NULL, but that should not cause any problems. 请注意,调用者必须以NULL终止元素列表,但这不会引起任何问题。 Refactored: 重构:

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);
  }
}

However, when I call it like this: 但是,当我这样称呼它时:

container_append(c, NULL);

... inside of container_vappend() , the call to va_arg() is returning something that isn't NULL. ...在container_vappend()内部,对va_arg()的调用返回的结果不是NULL。

This is a transcription of a more complicated function, but barring any typos, did I miss something in the setup or usage of va_list and va_arg() ? 这是一个更复杂的函数的副本,但是除非有任何错别字,否则我在设置或使用va_listva_arg()是否错过了某些东西吗?

In addition to issued identified well by @zwol ... 除了由@zwol确定的好 ...

container_append(c, one or more arguments, NULL); is potential undefined behavior (UB). 是潜在的不确定行为 (UB)。

container_append() expects a element_t *element and NULL may be simply 0 . container_append()期望element_t *elementNULL可能只是0

NULL which expands to an implementation-defined null pointer constant ... NULL扩展为实现定义的空指针常量...

An integer constant expression with the value 0, or such an expression cast to type void * , is called a null pointer constant . 值为0的整数常量表达式,或强制类型为void *的表达式,称为空指针常量

NULL is not specified even to be the same size as a pointer. 甚至不指定与指针大小相同的NULL

va_arg(ap, element_t *) is then UB. va_arg(ap, element_t *)是UB。

To make a safer call, use container_append(c, args, (element_t *) NULL); 为了进行更安全的调用,请使用container_append(c, args, (element_t *) NULL);

When container_append is called like this container_append像这样被调用时

 container_append(c, NULL); 

the named parameter element will be 0, and there won't be any anonymous parameters. named参数element将为0,并且不会有任何匿名参数。 Under those conditions, container_append must not call va_arg at all , or the program has undefined behavior. 在这些条件下, container_append不能调用va_arg 可言 ,或者该程序已未定义行为。 It happened to work by accident before you refactored the code, but the original code is just as buggy as the refactored version. 在重构代码之前,它偶然发生了问题,但是原始代码与重构版本一样有漏洞。

You can either check element before the loop... 您可以在循环之前检查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);
}

... or you can make all of the element arguments be anonymous: ...或者您可以将所有元素参数设为匿名:

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);
}

The latter structure is more compatible with the vappend refactor you want to do. 后一种结构与您要执行的vappend重构更兼容。


EDIT: Regarding this query in a comment: 编辑:关于此查询中的注释:

I thought va_start(ap, element) (in the caller) would set up va_arg to return element first. 我以为va_start(ap, element) (在调用方中)会设置va_arg首先返回元素。 Maybe it doesn't work that way? 也许这样行不通?

Indeed, it does not work that way. 确实,它不是那样工作的。 va_start sets up va_arg to return the first of the anonymous arguments. va_start设置va_arg返回第一个匿名参数。 If there weren't any anonymous arguments, then you walk off the end the very first time you call va_arg , and trigger UB. 如果没有任何匿名参数,则在第一次调用va_arg时触发结尾,并触发UB。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM