请考虑以下代码:
#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 %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以及它如何确定函数参数和局部变量的存储位置。更重要的是,这可能不是可移植代码。但我已经看到这个习惯用法在实际代码库中多次使用过。
答案 0 :(得分:7)
作为一个实际问题,我不知道它是多么“可移植”(从某种意义上说它是否会按照您在现有实现中的预期行为,或至少在您关注的实现子集中)
就C标准而言,它根本不可携带。您的程序具有未定义的行为,因为它通过类型(int(*)(int, int)
的表达式调用函数,该表达式与函数的实际类型(int(*)(int, int, int)
)不同。(前者是用于 define 您的add
函数;后者是用作调用前缀的表达式fn
的类型。)
这在C standard第6.5.2.2节第9段中说明:
如果使用与。不兼容的类型定义函数 表达式的表达式类型(表达式) 被称为函数,行为未定义。
(该链接指向C11标准的N1570草案的PDF。您可以在该标准的其他版本中找到类似或相同的措辞。)
我的建议:不要这样做。
但是,请注意, variadic 函数(使用printf()
声明的, ...
等函数)的多余参数会被忽略。例如,这是完全合法的:
printf("Ignore the arguments\n", 10, 20, 30);
如果你真的需要能够在不知道它期望多少个参数的情况下调用函数,这可能是一种可行的方法(尽管你将失去与, ...
匹配的任何参数的编译时类型检查)
对于非可变函数,您可以自由地将函数指针从一种类型转换为另一种类型,但是您必须为每次调用转换回正确的类型。