C 瓦拉格人是否提供了一种可怜的人';什么是多态性?

C 瓦拉格人是否提供了一种可怜的人';什么是多态性?,c,variadic-functions,C,Variadic Functions,毫无疑问,C系的所有其他学生都注意到了这一点;这对我来说是新鲜事 如果我声明: int xlate( void *, ... ); 然后以几种不同的方式定义xlate()(可能除了一种定义外,其他所有定义都是\ifdef-ed): 在每一篇文章中,都不要提及va_列表——永远不要提及它 xlate()的定义;然后调用xlate(),遵循其 有几个定义,似乎xlate()的每个编译版本 工作方式正是我想要的,至少在gcc和msvc下 C99是否保证了这种轻松、无要求、慷慨的编译器行为 谢谢 --

毫无疑问,C系的所有其他学生都注意到了这一点;这对我来说是新鲜事

如果我声明:

int xlate( void *, ... );
然后以几种不同的方式定义
xlate()
(可能除了一种定义外,其他所有定义都是
\ifdef
-ed):

在每一篇文章中,都不要提及va_列表——永远不要提及它 xlate()的定义;然后调用xlate(),遵循其 有几个定义,似乎xlate()的每个编译版本 工作方式正是我想要的,至少在gcc和msvc下

C99是否保证了这种轻松、无要求、慷慨的编译器行为

谢谢


--皮特

不,这更像是穷人的超载。C语言中的多态性(在多个对象类型上执行一个操作并让每个对象都做正确的事情的能力)通常是通过

你不能盲目地使用你喜欢的论据。要么你有一个固定的最小数量的参数,它可以告诉函数需要多少个变量,要么你在末尾有一个哨兵参数,表明你已经完成了

换句话说,类似于:

printf ("%d: %s\n", int1, charpointer2);
x = sum_positive_values (1, 2, 3, 4, 5, -1);

如果您将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; } } 枚举{ 杜福, 杜巴, }; 无效xlat(枚举tp类型,…) { 结构foo*foo; 结构条*bar; va_列表argp; va_启动(argp,类型); if(type==T_FOO){ foo=va_arg(argp,结构foo*); 和阿福做点什么; }else if(类型==T_BAR){ bar=va_arg(argp,结构bar*); 用吧台做点什么; } }
虽然我想这更像是重载而不是多态性。

不是编译器让这项有趣的业务发挥作用,而是编译平台的ABI。可变函数调用使用与任何其他函数调用相同的规则,因此,如果传递正确数量和类型的参数,即使调用函数认为它是可变的,接收函数也能够正确理解参数(即使接收函数不是可变的)。

否,该标准不保证此类行为。相关文本见§6.5.2.2:

9如果函数是用 类型,该类型与 指向的(表达式的)类型 表示被调用对象的表达式 函数,则行为未定义

一些平台在调用varargs函数时需要使用不同的调用约定,因为它们通常的调用约定要求被调用方知道传递了多少实际参数。编写C标准时特别考虑到这一点,因此只能通过正确类型的varargs声明调用varargs函数,而表示varargs函数的表达式只能用于调用varargs函数


您可以通过创建每个函数的匹配声明来做您想做的事情,该声明包含在相同的
#ifdef
魔术中,该魔术选择了一个也用于选择正确函数定义的声明。

除了caf的答案之外:

由于存在一些问题,这无法正常工作,标准除了禁止这样的事情外,没有其他办法:

原型告诉调用方调用函数时必须如何执行参数转换。对于第一个参数,您给出的示例已经不能可靠地工作。您先将其声明为
void*
,然后在另一个中声明为
int
。由于两者的宽度可能不同,您的代码在大多数64位体系结构上注定会失败

更糟糕的是,
符号告诉调用方对其余参数应用默认升级。例如,如果您的实现需要一个
浮点值
,那么调用方总是会提供一个
双精度
,并且,您的代码会严重崩溃(=最近)


然后,现代体系结构有复杂的规则,它们将哪种类型的参数放在堆栈上,哪种类型的参数保存在寄存器中。这取决于参数的类型,例如整数和浮点具有不同的寄存器集。因此,这将使您的参数完全错误。

请下次格式化您的代码。因为void*可以接受任何值,所以我认为接收函数(即xlate)可以以任何方式理解/使用/解释任何参数。事实上,这就是重点。谢谢你的评论。我希望听到C99承诺这样的行为。是的,它超载了。固定最小参数数?考虑到我对变量函数知之甚少,我觉得这不对。但是,是的,必须至少有一个arg。修正了最大数值。看,我担心退出函数的ret是否会使堆栈保持良好状态——您的评论与此有关。我指的是固定最小值,如
printf
(固定最小值包括“一”,但可能还有更多,如
[fs]printf
)-您需要固定参数来告诉您它们还有多少个参数,无论是计数还是带有
%
标记的字符串。是的,我同意“一”是一个数字:-)。但是,看,我通过实验观察到,我确实可以使用任意数量的arg调用func,并且无论预期使用多少arg,func都会正确地运行(并返回)。为了好玩,请尝试您的printf示例,稍微修改:printf(“%d:%s%s%s%s%s%s%s%s%s%s\n”,int1,charpointer2);它工作得很好!不,不要那样做。如果它起作用,那完全是偶然的。提供比
%
标记更多的参数通常是可以的,但反过来就不太好了。重载是一种多态机制(在源代码级别,而不是二进制)。是的,这是正确的。说得好:调用者认为函数是可变的,但有趣的是 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; } }