【问题标题】:Calling a C Function without Prototype [duplicate]在没有原型的情况下调用 C 函数 [重复]
【发布时间】:2011-12-15 19:05:15
【问题描述】:

我有一个调用函数的 C 文件(比如file1.cfun1(1,b).

这个函数fun1(int a,int b) 驻留在另一个C 文件中(比如file2.c),但它的原型不包含在头文件中(比如file2.h)。 file2.h 包含在 file1.c 中。

我的问题是,如果我从file1.c 调用fun1(a,b),它会通过将控制权传递给file2.c 中的函数定义来工作吗?或者会发生异常或预期的行为是什么?

我是否必须在file2.h 中提供fun1(int a, int b) 的原型才能使其工作?

【问题讨论】:

    标签: c function function-prototypes


    【解决方案1】:

    如果目标文件链接在一起,预期的行为是您的函数将被调用。任何未声明的函数都被假定为一个外部函数,它返回一个用于编译目的的整数,以及一个用于链接目的的外部符号(到目标文件)。我给你举个具体的例子:

    foo.c:

    void foo(const char *name) {
        printf("foo called with %s\n", name);
    }
    

    bar.c:

    void bar(int a) {
        printf("bar called with %d\n", a);
    }
    

    main.c:

    int main(int argc, char *argv[]) {
        foo("Hello");
        bar(5);
        return 0;
    }
    

    使用 gcc 编译目标文件:

    gcc -fno-builtin -ansi -c -o foo.o foo.c
    gcc -fno-builtin -ansi -c -o bar.o bar.c
    gcc -fno-builtin -ansi -c -o main.o main.c
    

    这些不应产生任何警告或错误

    现在将它们链接在一起:

    gcc -o progy main.o bar.o foo.o 
    

    请注意,我使用 gcc 来链接二进制文件,但这相当于:

    ld -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o progy  /usr/lib64/crt1.o /usr/lib64/crti.o /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.1/crtbegin.o main.o foo.o bar.o -lc  -L/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.1 -lgcc -lgcc_s /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.1/crtend.o /usr/lib64/crtn.o
    

    在我的 Linux 64 位平台上。 (GCC 实际上是这样运行 LD 来进行链接的)

    使用您的 C 编译器(在我的情况下为 gcc)进行链接将确保无论您的构建目标平台是什么,都可以正确调用链接器。如果您使用 IDE,那么这些步骤都会被一个漂亮的界面隐藏起来。

    【讨论】:

    • 您的代码示例在 C89 中导致未定义的行为,因为在范围内调用没有原型的函数会导致返回类型为 int 的隐式声明;但是该函数实际上返回void。在 C99 和 C11 中,它的格式不正确。
    【解决方案2】:

    根据情况和您的编译器,可能会发生一些事情:

    • 您收到编译错误。编译器举起双手拒绝生成目标文件。
    • 编译器将函数声明视为调用所隐含的,并继续进行链接。它可能假设您知道您在函数参数方面正在做什么。通常它也假定一个 int 返回类型。几乎我使用过的每个编译器都会在执行此操作时产生警告。
    • 编译器将声明视为调用所隐含的,但无法链接。与上面类似,但链接器随后注意到您尝试调用的隐含函数与您实际编写的函数不同并且死亡。

    无论如何,您都应该提供原型。

    【讨论】:

    • 原型的声明和头文件的包含与生成二进制文件的链接阶段无关。任何 C 编译器最多只会抛出一个警告。您将收到警告的时间,例如使用 gcc,是如果您打开了缺少原型的警告标志,即使这样,它也只会针对全局函数引发,并且仅针对实现该函数且没有正式原型的源文件。 ANSI C 特别允许在没有原型的情况下调用函数。
    • @Ahmed 我同意你的观点,它没有直接关系,但我的观点是,通过不向编译器提供原型,你删除了一层检查。如果您与原型不匹配,编译器会给您一个错误。如果您没有原型,那么它会由链接器来捕获错误。你想要编译器捕捉错误,所以给它一个原型,这样它就可以完成它的工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    • 2014-02-03
    • 1970-01-01
    相关资源
    最近更新 更多