2

有一件事总是让我感到困惑va_end()。我经常读到这不是一个实际的函数,而是一个预处理器宏。尽管这听起来像是一个微不足道的细节,但它实际上可能会影响va_end()需要调用的位置。

问题很简单:是否va_end()需要return在具有多个return语句的可变参数函数中的每个语句之前调用?

以下示例中的可变参数函数具有返回NULL其参数的第一个非参数的简单任务。如您所见return,函数体中有三个语句。其中之一出现在 之后va_start()但之前va_end()

这段代码正确吗?

#include <stdio.h>
#include <stdarg.h>

static const void * const FIRST_NON_NULL_END = (void *) "";

void * first_non_null (
    const void * const ptr1,
    ...
) {

    if (ptr1) {

        return ptr1 == FIRST_NON_NULL_END ? NULL : (void *) ptr1;

    }

    void * retval;
    va_list args;
    va_start(args, ptr1);

    do {

        retval = va_arg(args, void *);

        if (retval == FIRST_NON_NULL_END) {

            /*  Is this correct? Here I do not invoke `va_end()`!  */
            return NULL;

        }

    } while (!retval);

    va_end(args);
    return retval;

}

int main () {

    const char * const my_string = first_non_null(
        NULL,
        NULL,
        "autumn",
        NULL,
        "rose",
        FIRST_NON_NULL_END
    );

    printf("The first non-null value is: \"%s\".\n", my_string);

    return 0;

}

编辑:

为了澄清起见,上面的示例仅出于教学目的以这种形式编写。在现实生活中,它可以以更好和综合的方式重写为:

#include <stdio.h>
#include <stdarg.h>

static const void * const FIRST_NON_NULL_END = (void *) "";

void * first_non_null (
    const void * const ptr1,
    ...
) {

    const void * retval;
    va_list args;
    va_start(args, ptr1);

    for (retval = ptr1; !retval; retval = va_arg(args, const void *))
        ;;

    va_end(args);
    return retval == FIRST_NON_NULL_END ? NULL : (void *) retval;

}

int main () {

    const char * const my_string = first_non_null(
        NULL,
        NULL,
        "autumn",
        NULL,
        "rose",
        FIRST_NON_NULL_END
    );

    printf("The first non-null value is: \"%s\".\n", my_string);

    return 0;

}
4

1 回答 1

4

来自 C 标准(7.16.1 变量参数列表访问宏)

1 ... va_start 和 va_copy 宏的每次调用都应与同一函数中 va_end 宏的相应调用相匹配。

因此,如果va_start在任何 return 语句之前使用并且va_end尚未调用,则应调用它。

于 2021-09-03T20:05:22.217 回答