调用带有额外参数的C函数是否可移植



考虑以下代码:

#include <stdio.h>
typedef int (*addif_fn_t) (int, int, int);
int add (int a, int b) {
    return a + b;
}
int addif (int a, int b, int cond) {
    return cond ? (a + b) : 0;
}
int main() {
    addif_fn_t fn;
    fn = addif;
    printf("addif:t%d %dn", fn(1, 2, 1), fn(1, 2, 0));
    fn = (addif_fn_t)add;
    printf("add:t%d %dn", fn(1, 2, 1), fn(1, 2, 0));
    return 0;
}

在任何使用标准C调用约定的Intel机器上,结果是:

addif:  3 0
add:    3 3

问题是:这个习语的可移植性如何?C允许调用比它接受的参数更多的函数吗?

我的猜测是,这完全取决于ABI以及它如何确定函数参数和局部变量存储的位置。更重要的是,这可能不是可移植的代码。但是我在实际的代码库中看到过这种习惯用法。

作为一个实际问题,我不知道它有多"可移植"(在某种意义上,它是否会像您所期望的那样在现有实现下运行,或者至少在您所关心的实现子集下运行)。

就C标准而言,它根本不能移植。您的程序具有未定义的行为,因为它通过类型(int(*)(int, int))的表达式调用函数,该表达式与函数的实际类型(int(*)(int, int, int))不同。(前者是用于定义 add函数的类型;后者是作为调用前缀的表达式fn的类型。)

这在C标准第6.5.2.2节第9段中有规定:

如果函数定义的类型与的表达式所指向的(表达式的)类型被调用的函数,行为未定义。

(链接是C11标准的N1570草案的PDF。您可以在其他版本的标准中找到类似或可能相同的措辞。

我的建议:不要那样做。

但是,请注意,可变函数(像printf()这样用, ...声明的函数)的多余参数会被忽略。例如,这完全合法:
printf("Ignore the argumentsn", 10, 20, 30);

如果你真的需要能够在不知道函数需要多少参数的情况下调用它,这可能是一个可行的方法(尽管你会失去对任何匹配, ...的参数的编译时类型检查)。

对于非可变函数,可以自由地将函数指针从一种类型转换为另一种类型,但每次调用都必须转换回正确的类型。

最新更新