为什么将额外的参数传递给C函数不会导致编译时错误?

时间:2014-12-05 20:18:02

标签: c gcc compiler-errors linker

我能够使用gcc 4.4.7编译并运行以下代码。

档案:m.c

#include <stdio.h>

int main()
{
    printf("%d\n", f(1, 2, 3));
}

档案:f.c

int f(int a, int b)
{
    return a + b;
}

输出:

$ gcc m.c f.c && ./a.out
$ 3

当在同一文件中定义函数f()时,编译器会按预期引发错误。我的猜测是编译器无法检测编译单元之间错误的函数使用情况。但链接器不应该能够检测到它吗?标准是否指定了预期的行为?

请注意,这与声明没有任何参数的函数不同,即使在单个文件中也是如此。 (Why does gcc allow arguments to be passed to a function defined to be with no arguments?)。

我使用的是gcc(GCC)4.4.7 20120313(Red Hat 4.4.7-11)和GNU ld版本2.20.51.0.2-5.42.el6 20100205。

2 个答案:

答案 0 :(得分:3)

默认情况下,

gcc目前使用-std=gnu89进行编译(不确定是否为5.x,但之前的版本确实如此)。在C89(GNU89几乎是C89-superset)中,如果在没有声明可见的情况下调用函数,则假定它被声明为

extern int f();

具有外部链接的函数,返回int,并接受未指定(但固定)的默认提升参数。

这被许多人认为是一个设计错误,在C89中被标记为过时,并最终在C99中删除。默认情况下,Gcc会对隐式函数声明发出警告。

如果使用错误类型或数量的参数调用函数,则行为未定义,仅当prototype-declaration在调用范围内时才需要诊断。该标准对实现的作用没有要求,链接器将被允许失败(但通常不会)。

使用包含原型的头文件来获取警告。

通常,C编译器将源文件分别编译为目标文件,其中存储函数的符号时没有任何关于其参数类型的信息,因此链接器无法检查它们。

答案 1 :(得分:1)

编辑:

我想我最初误会了:

&#34;当没有函数声明时,gcc不知道在编译时阶段会发生什么,只要它在链接阶段找到函数,一切都会起作用。这个&#39;错误&#39;必须在编译阶段被捕获,因为从技术上讲,它在链接阶段根本不是错误。&#34;

老答案:

这是一项功能而非错误。调用函数时,参数会被压入堆栈。如果你不使用它们,一般没什么大不了的。

您甚至可以计划拥有未知数量的参数。以下是用于记录的自定义printf样式函数的简单示例:

void Debug_Message(uint32_t level, const char *format, ...)
{
    char buffer[256];

    //check level and do stuff

    va_list args;
    va_start(args, format);
    vsnprintf(buffer, sizeof(buffer), format, args);

   //buffer now contains data as if we did an sprintf to it
}

这就像printf一样被调用,可能是:

Debug_Message(1, "%d%d%d", 1, 2, 3);

可能是:

Debug_Message(1, "%d", 1);