为什么我可以在 C 中调用函数而不声明它,但不能在 C++ 中调用它

Why can I call a function in C without declaring it but not in C++?

本文关键字:调用 但不能 C++ 声明 函数 我可以 为什么      更新时间:2023-10-16

在C++中,在声明函数之前调用函数是一个编译器错误。但在 C 中,它可以编译。

#include<stdio.h>
int main()
{
   foo(); // foo() is called before its declaration/definition
} 
int foo()
{
   printf("Hello");
   return 0; 
} 

我已经尝试过并知道它是正确的,但我无法理解它背后的原因。任何人都可以解释编译过程实际上是如何进行的,并且在两种语言中有所不同。

代码">

编译"为c程序的事实并不意味着你可以做到这一点。编译器应警告函数foo()隐式声明

在这种特殊情况下,隐式声明将声明相同的foo()并且不会发生任何不好的事情。

但假设以下情况,假设这是

主.c

/* Don't include any header, why would you include it if you don't
   need prototypes? */
int main(void)
{
    printf("%dn", foo()); // Use "%d" because the compiler will
                           // implicitly declare `foo()` as
                           //
                           //              int foo()
                           //
                           // Using the "correct" specifier, would
                           // invoke undefined behavior "too".
    return 0;
}

现在假设foo()在不同的编译单元1foo.c 中定义为

福克

double foo()
{
    return 3.5;
}

它是否按预期工作?

你可以想象如果你使用malloc()而不包括stdio.h会发生什么,这与我上面试图解释的情况几乎相同。

因此,

这样做将调用未定义的行为2,因此在这种情况下,术语">Works"在可理解的意义上不适用。

这可以编译的原因是,在过去,c标准(即c89标准(允许它。

c++ 标准从未允许这样做,因此如果您在调用之前调用代码中没有原型(">声明"(的函数,则无法编译 c++ 程序。

现代 c 编译器对此发出警告,

因为很容易发生未定义行为的可能性,并且由于忘记添加原型或包含适当的标头并不难,因此如果编译器可以对此发出警告,而不是突然出现一个非常莫名其妙的错误,对程序员来说更好。


1它不能在同一个文件中编译,因为它将使用不同的返回类型定义,因为它已经隐式声明

2doubleint是不同的类型开始,因此会有未定义的行为。

当 C 被开发出来时,函数名称是你能够调用它所需要的。将参数与函数参数匹配严格来说是程序员的事;编译器并不关心您是否将三个浮点数传递给只需要一个整数的东西。

然而,事实证明这很容易出错,所以后来的 C 语言迭代添加了函数原型作为(仍然是可选的(附加限制。C++这些限制进一步收紧:现在功能原型始终是强制性的。

我们可以推测原因,但部分原因是因为C++仅仅知道函数名称已经不够了。可以有多个具有相同名称但具有不同参数的函数,编译器必须确定要调用哪个函数。它还必须弄清楚如何调用(直接还是虚拟?(,甚至可能必须在模板函数的情况下生成代码。

鉴于所有这些,我认为让语言要求在调用函数时知道函数原型是有意义的。

最初,C 没有函数原型,C++也不存在。

如果你说

extern double atof();

这表示atof是一个返回双精度的函数。 (关于它的论点,什么也没说。

如果你然后说

double d = atof("1.3");

它会起作用。 如果你说

double d2 = atof();    /* whoops, forgot the argument to atof() */

编译器不会抱怨,但如果你尝试运行它,就会发生一些奇怪的事情。

在那些日子里,如果你想捕获与使用错误数量或类型的参数调用函数相关的错误,那就是一个单独的程序的工作,lint,而不是C编译器。

同样在那些日子里,如果你只是突然调用了一个编译器以前从未听说过的函数,比如这样:

int i = atoi("42");

编译器基本上假装你之前说过

extern int atoi();

这就是所谓的隐式函数声明。 每当编译器看到对它不知道名称的函数的调用时,编译器就会认为这是一个返回 int 的函数。

快进几年。 C++发明了我们今天所知道的功能原型。 除此之外,它们还允许您声明函数所需的参数的数量和类型,而不仅仅是其返回类型。

再快进几年,C 语言可以选择采用功能原型。 如果需要,可以使用它们,但如果不这样做,编译器仍将对它看到的任何未知函数调用执行隐式声明。

再快进几年,到 C11。 现在隐式 int 终于消失了。 如果调用函数而不先声明它,则需要编译器进行投诉。

但即使在今天,您可能正在使用 C11 之前的编译器,它仍然对隐式 int 感到满意。 C11 投诉编译器可能会选择仅发出警告(而不是编译终止错误(,如果您忘记在调用函数之前声明它。 符合 C11 标准的编译器可能会提供一个选项来关闭这些警告,并悄悄地接受隐式整数。 (例如,当我使用非常现代的 clang 时,我会安排用 -Wno-implicit-int 调用它,这意味着我不想要关于隐式 int 的警告,因为我仍然有很多旧的、我不想重写的工作代码。

为什么我可以在不声明的情况下调用 C 中的函数?

因为在 C 中,而不是在 C++ 中,假设没有原型的函数返回int

这是该函数的隐式声明。 如果该假设被证明是正确的(该函数稍后以返回类型int声明(,则程序可以很好地编译。

如果该假设被证明是假的(例如,假设它返回一个int,但实际上被发现返回一个double(,那么你会收到一个编译器错误,两个函数不能具有相同的名称。 (例如。 int foo()double foo()不能同时存在于同一个程序中(

请注意,所有这些都只是 C
在C++中,不允许隐式声明。即使它们是,错误消息也会有所不同,因为C++具有函数重载。该错误会说函数的重载不能仅因返回类型而异。(重载发生在参数列表中,而不是返回类型(

相关文章: