在没有原型的情况下调用C函数

在没有原型的情况下调用C函数,c,function,function-prototypes,C,Function,Function Prototypes,我有一个调用函数的C文件(比如file1.C) fun1(1,b) 此函数fun1(inta,intb)驻留在另一个C文件(比如file2.C)中,但其原型不包含在头文件中(比如file2.h)。 file2.h包含在file1.c中 我的问题是,如果我从file1.c调用fun1(a,b),它会通过将控制权传递给file2.c中的函数定义来工作吗?或者会发生异常,或者预期的行为是什么 我是否必须在file2.h中给出fun1(inta,intb)的原型才能工作?根据具体情况和编译器,可能会发生

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

此函数
fun1(inta,intb)
驻留在另一个C文件(比如
file2.C
)中,但其原型不包含在头文件中(比如
file2.h
)。
file2.h
包含在
file1.c

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


我是否必须在
file2.h
中给出
fun1(inta,intb)
的原型才能工作?

根据具体情况和编译器,可能会发生以下几种情况:

  • 您会得到一个编译错误。编译器举起手臂拒绝生成对象文件
  • 编译器将函数声明视为调用所暗示的,并继续进行链接。它可能假设您知道您在函数参数方面做了什么。通常它还假定为int返回类型。几乎我使用过的每个编译器在执行此操作时都会产生警告
  • 编译器将声明视为调用隐含的声明,但无法链接。与上面一样,但是链接器会注意到您试图调用的隐含函数与您实际编写的函数不同,并且会终止

不管怎样,您都应该提供一个原型。

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

foo.c:

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

void bar(int a) {
    printf("bar called with %d\n", a);
}
主要条款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,那么这些步骤都会被一个漂亮的界面隐藏。

原型声明和头文件包含与生成二进制文件的链接阶段无关。任何C编译器最多只会抛出一个警告。您将收到警告的时间(例如使用gcc)是如果您启用了missing prototype警告标志,并且仅针对全局函数和实现该函数且没有正式原型的源文件发出警告标志。ANSI C特别允许在不存在原型的情况下调用函数。@Ahmed我同意你的观点,它与编译器没有直接关系,但我的观点是,如果不向编译器提供原型,就可以消除一级检查。如果与原型不匹配,编译器会给您一个错误。如果您没有原型,那么就由链接器来捕获错误。您希望编译器捕获错误,所以给它一个原型,这样它就可以完成它的工作。您的代码示例在C89中会导致未定义的行为,因为在范围中调用没有原型的函数会导致返回类型为
int
的隐式声明;但是,函数实际上返回
void
。在C99和C11中,它是病态的。