这是将 va_arg 与函数指针一起使用的正确方法吗?

Is this the correct way to use va_arg with pointer to function?

在这样的函数中:

typedef double(*dfun)(double);

void tab(double x, int n, ...)
{
    va_list args;

    va_start(args, n);

    printf("%5.2lf  \t", x);

    for (int i=0; i<n; i++)
    {
        dfun tmp = va_arg(args, dfun);

        printf("  %5.2lf  \t", tmp(x));
    }

    va_end(args);
}

我这样拉论据是不是错了:

    double(*tmp)(double) = va_arg(args, double(*)(double));

我看到了 this 文章,其中建议了一种不同的方法,如果它不起作用:

Q: I can't get va_arg to pull in an argument of type pointer-to-function.

A: Try using a typedef for the function pointer type. The type-rewriting games which the va_arg macro typically plays are stymied by overly-complicated types such as pointer-to-function.

就我而言,它适用于两个版本 (GCC 5.2.1),这就是为什么我想知道一种方法是否比另一种更好?当我不首先对函数指针进行类型定义时,是否存在一些潜在的错误?

在某些情况下使用没有 typedef 的指针确实会导致问题。

标准说明 va_arg 中使用的 type 必须以某种方式编写,其中添加后缀 * 将创建指向 type 的指针:

7.16.1.1 The va_arg macro

  1. The parameter type shall be a type name specified such that the type of a pointer to an object that has the specified type can be obtained simply by postfixing a * to type.

因此要有兼容的代码,当使用函数指针时,你应该使用 typedef,因为 * 不能添加到函数指针类型作为后缀来创建指向该类型的指针,因为语法无效:

(double)(*)(double) => (double)(*)(double)*

但它可以添加到 typedef:

dfun => dfun*

va_arg(args, type) 记录为(参见 ISO 9899:2011 §7.16.1.1 ¶2):

The va_arg macro expands to an expression that has the specified type and the value of the next argument in the call. The parameter ap shall have been initialized by the va_start or va_copy macro (without an intervening invocation of the va_end macro for the same ap). Each invocation of the va_arg macro modifies ap so that the values of successive arguments are returned in turn. The parameter type shall be a type name specified such that the type of a pointer to an object that has the specified type can be obtained simply by postfixing a * to type. If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:

  • one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;
  • one type is pointer to void and the other is a pointer to a character type.

因此,va_arg(args, double(*)(double)) 不是对 va_args 的有效调用,因为 double(*)(double)* 是一个语法错误,而不是指向 double(*)(double) 的指针类型 double(*)(double) =21=]。因此,需要类型定义以符合标准。 GNU C 编译器是仁慈的,因为它不需要这种语法限制,但您的代码可能无法与其他编译器一起编译。