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

请考虑以下代码:

#include  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 %d\n", fn(1, 2, 1), fn(1, 2, 0)); fn = (addif_fn_t)add; printf("add:\t%d %d\n", 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。您可以在该标准的其他版本中找到类似或相似的措辞。)

我的建议:不要那样做。

但是请注意,对于可变参数函数的多余参数(使用a , ...声明的函数,如printf() )会被忽略。 例如,这是完全合法的:

 printf("Ignore the arguments\n", 10, 20, 30); 

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

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