毫无疑问,C的每个其他学生都注意到了这一点;这对我来说是新的。
如果我宣布:
int xlate( void *, ... );
然后以几种不同的方式定义xlate( )
(可能所有定义都有#ifdef
- ed out):
int xlate ( char *arg1 ) { ... }
int xlate ( int arg1, char *arg2, int arg3 ) { ... }
int xlate ( char arg1, int *arg2 ) { ... }
并且省略任何提及va_list - 从不提及它 - 在每一个 xlate()的定义;然后调用xlate()遵守其中一个 几个定义,似乎每个编译版本的xlate() 按照我想要的方式工作,至少在gcc和msvc下。
在C99下保证这种放松,不严格,慷慨的编译器行为吗?
谢谢!
- 皮特
答案 0 :(得分:4)
不,这更像是一个穷人的重载。 C中的多态性(对多个对象类型执行操作并且每个对象都执行正确的操作的能力)通常使用structures containing function pointers完成。
你不能只是盲目使用尽可能多的参数。要么你有一个固定的最小数量的参数可以告诉函数有多少变量可以预期,或者你有一个endinel参数在最后表明你已经完成。
换句话说,比如:
printf ("%d: %s\n", int1, charpointer2);
x = sum_positive_values (1, 2, 3, 4, 5, -1);
答案 1 :(得分:3)
如果您声明xlat为void *
,那么您不能只使用int
来实现它。即使是穷人的超载也要做得恰到好处,而且可能看起来像
enum { T_FOO, T_BAR, }; void xlat(enum tp type, ...) { struct foo *foo; struct bar *bar; va_list argp; va_start(argp, type); if (type == T_FOO) { foo = va_arg(argp, struct foo *); do_something_with_foo; } else if (type == T_BAR) { bar = va_arg(argp, struct bar *); do_something_with_bar; } }
虽然我猜这更像是重载而不是多态。
答案 2 :(得分:3)
不,标准保证不这种行为。相关文本见§6.5.2.2:
9如果用a定义了该功能 与...不兼容的类型 指向的类型(表达式) 表示被调用的表达式 功能,行为未定义。
某些平台在调用varargs函数时需要使用不同的调用约定,因为它们通常的调用约定要求被调用者知道传递了多少实际参数。 C标准是专门为此编写的 - 所以varargs函数只能通过正确类型化的varargs声明来调用,而表示varargs函数的表达式只能用于调用varargs函数。
您可以通过创建每个函数的匹配声明来执行您想要的操作,包含在相同的#ifdef
魔法中,选择一个也用于选择正确函数定义的魔法。
答案 3 :(得分:3)
除了caf的回答:
由于存在一些问题,这不起作用,标准除了禁止这样的事情之外别无能为力:
原型告诉调用方在调用函数时必须如何执行参数转换。您提供的示例已经不能可靠地用于第一个参数。您将其声明为void*
,然后将int
声明为另一个。由于两者的宽度可能不同,因此在大多数64位架构中,您的代码都会失败。
更糟糕的是,...
符号告诉主叫方对其余参数应用默认促销。例如,如果您的实现期望float
主叫方将始终提供double
,并且您的代码将再次严重崩溃(=最近)。
然后,现代体系结构具有复杂的规则,它们将哪些类型的参数放在堆栈中并保存在寄存器中。这取决于参数的类型,例如整数和浮点具有不同的寄存器集。所以这会让你的论点完全错误。
答案 4 :(得分:2)
编译器使得这个有趣的业务工作不是因为它是您正在编译的平台的ABI。使用与任何其他函数调用相同的规则进行可变函数调用,因此如果传递正确的参数数量和类型,即使调用函数认为它是可变参数,接收函数也能够理解正确的参数(即使接收函数不是可变参数)。