这种可疑的非原型函数声明的使用是否有效?

时间:2010-11-14 19:57:44

标签: c prototype standards language-lawyer

这是有效的C(C99)代码吗?

int f();
int g(int x)
{
    if (x<0) return f(x);
    else return f(x,x);
}

显然,如果使用否定参数调用g并且f不是采用单个int参数的函数,或者g,则程序具有未定义的行为用非负参数调用,f不是一个带有两个int参数的函数。但除此之外?

请考虑这个单独的源文件作为示例,该文件从上面调用g并提供f

int g();
#ifdef FOO
int f(int a, int b) { return a+b; }
int main() { return g(1); }
#else
int f(int a) { return a; }
int main() { return g(-1); }
#endif

5 个答案:

答案 0 :(得分:4)

让我们反过来问:为什么有效?。我真的找不到任何禁止上述代码的论点或规则。从不执行相应其他分支中的函数调用(尽管注释中的讨论表明它并不那么容易!)。

答案 1 :(得分:3)

C99(6.5.2.2函数调用,第8项)表示如果函数定义没有原型,则“不比较”参数和参数的数量和类型。

我已经看到这个(ab)在野外使用了函数指针。 void (*)()数组包含void (*)(struct Client *)void (*)(struct Client *, int parc, char *parv[])函数指针。根据数组索引,代码是否传递了额外的参数。

在这种情况下,编译器没有(合理的)方法提前检查参数的数量,即使它具有所有相关的代码。

我认为这是脏代码,我修复了那个特定的实例。

答案 2 :(得分:2)

我同意它是有效的,只要不正确的函数调用永远不会被C抽象机器评估。

有另一种更简单的方法可以得出关于链接器的结论:因为这是允许的:

int f();
int (*fp)() = f;

链接器必须能够在不知道其实际定义的情况下找到f()的地址。因此,必须能够在不知道实际定义的情况下确定其符号。

答案 3 :(得分:0)

如果它是f(int x, ...)并且它看到它的第一个参数的符号知道它得到了多少(0或1)varargs怎么办?

答案 4 :(得分:0)

它有效(嗯,这可能取决于您使用的是哪种标准)。您应该阅读有关calling conventions的内容。

基本上,如果f有一个或没有参数,我认为没有问题 如果f接受两个或多个参数,那么其他参数(除了第一个参数之外)可能会有垃圾(显然是随机的)值。

考虑这段代码:

int f(int x, int y);
int g(int x)
{
   int k; //No value
   if (x<0) return f(x, k);
   else return f(x, x);
}

当然,这是一个坏主意。您应该更愿意显式声明所有参数。

您也可以使用int f(void);明确声明f不带参数。

请注意,C ++的函数重载可能会导致问题,但我认为这不是问题,因为您使用c标记了问题。 此外,一些调用约定可能会导致严重问题。