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

时间:2022-03-28 16:56:50

In C++, it is a compiler error to call a function before it is declared. But in C, it may compile.

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

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

int foo()
{
   printf("Hello");
   return 0; 
} 

I have tried and know that it is correct but I can't get the reason behind it. Can anyone please explain how the compilation process actually takes place and differs in both the languages.

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

4 个解决方案

#1


11  

The fact that the code "compiles" as a program doesn't mean you can do it. The compiler should warn about implicit declaration of the function foo().

代码“编译”为c程序的事实并不意味着你可以做到。编译器应该警告函数foo()的隐式声明。

In this particular case implicit declaration would declare an identical foo() and nothing bad will happen.

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

But suppose the following, say this is

但假设有以下情况,请说明这一点

main.c

/* Don't include any header, why would you include it if you don't
   need prototypes? */

int main(void)
{
    printf("%d\n", foo()); // Use "%d" because the compiler will
                           // implicitly declare `foo()` as
                           //
                           //              int foo()
                           //
                           // Using the "correct" specifier, would
                           // invoke undefined behavior "too".
    return 0;
}

Now suppose foo() was defined in a different compilation unit1foo.c as

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

foo.c

double foo()
{
    return 3.5;
}

does it work as expected?

它是否按预期工作?

You could imagine what would happen if you use malloc() without including stdio.h, which is pretty much the same situation I try to explain above.

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

So doing this will invoke undefined behavior2, thus the term "Works" is not applicable in the understandable sense in this situation.

因此,执行此操作将调用未定义的行为2,因此术语“Works”在这种情况下不适用于可理解的意义。

The reason this could compile is because in the very old days it was allowed by the standard, namely the standard.

这可以编译的原因是因为在过去很久以前它被c标准所允许,即c89标准。

The standard has never allowed this so you can't compile a program if you call a function that has no prototype ("declaration") in the code before it's called.

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

Modern compilers warn about this because of the potential for undefined behavior that can easily occur, and since it's not that hard to forget to add a prototype or to include the appropriate header it's better for the programmer if the compiler can warn about this instead of suddenly having a very unexplicable bug.

现代c编译器警告这一点,因为可能容易发生未定义的行为,并且由于忘记添加原型或包含适当的头并不难,如果编译器可以对此进行警告而不是突然间有一个非常难以理解的错误。


1It can't be compiled in the same file because it would be defined with a different return type, since it was already implicitly declared

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

2Starting with the fact that double and int are different types, there will be undefined behavior because of this.

2启动double和int是不同类型的事实,因此会有未定义的行为。

#2


5  

When C was developed, the function name was all you needed to be able to call it. Matching arguments to function parameters was strictly the business of the programmer; the compiler didn't care if you passed three floats to something that needed just an integer.

在开发C时,函数名称就是您需要能够调用它的全部名称。函数参数的匹配参数完全是程序员的业务;编译器并不关心你是否将三个浮点数传递给只需一个整数的东西。

However, that turned out to be rather error prone, so later iterations of the C language added function prototypes as a (still optional) additional restriction. In C++ these restrictions have been tightened further: now a function prototype is always mandatory.

然而,事实证明这很容易出错,因此后来C语言的迭代添加了函数原型作为(仍然是可选的)附加限制。在C ++中,这些限制已经进一步加强:现在函数原型总是强制性的。

We can speculate on why, but in part this is because in C++ it is no longer enough to simply know the function name. There can be multiple functions with the same name, but with different arguments, and the compiler must figure out which one to call. It also has to figure out how to call (direct or virtual?), and it may even have to generate code in case of a template function.

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

In light of all that I think it makes sense to have the language demand that the function prototype be known at the point where the function is called.

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

#3


2  

Originally, C had no function prototypes, and C++ did not exist.

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

If you said

如果你说

extern double atof();

this said that atof was a function returning double. (Nothing was said about its arguments.)

这说是atof是一个返回双重的函数。 (关于其论点没有任何说法。)

If you then said

如果你那么说

double d = atof("1.3");

it would work. If you said

它会奏效。如果你说

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

the compiler would not complain, but something weird would happen if you tried to run it.

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

In those days, if you wanted to catch errors related to calling functions with the wrong number of arguments, that was the job of a separate program, lint, not the C compiler.

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

Also in those days, if you just called a function out of the blue that the compiler had never heard of before, like this:

同样在那些日子里,如果你刚刚调用了编译器之前从未听说过的函数,就像这样:

int i = atoi("42");

the compiler basically pretended that earlier you had said

编译器基本上假装你早先说过

extern int atoi();

This was what was called an implicit function declaration. Whenever the compiler saw a call to a function whose name it didn't know, the compiler assumed it was a function returning int.

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

Fast forward a few years. C++ invents the function prototypes that we know today. Among other things, they let you declare the number and types of the arguments expected by a function, not just its return type.

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

Fast forward a few more years, C adopts function prototypes, optionally. You can use them if you want, but if you don't, the compiler will still do an implicit declaration on any unknown function call it sees.

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

Fast forward a few more years, to C11. Now implicit int is finally gone. A compiler is required to complain if you call a function without declaring it first.

快进几年,到C11。现在隐式int终于消失了。如果在没有先声明函数的情况下调用函数,则需要编译器进行投诉。

But even today, you may be using a pre-C11 compiler that's still happy with implicit int. And a C11-complaint compiler may only issue a warning (not a compilation-killing error) if you forget to declare a function before calling it. And a C11-compliant compiler may offer an option to turn off those warnings, and quietly accept implicit ints. (For example, when I use the very modern clang, I arrange to invoke it with -Wno-implicit-int, meaning that I don't want warnings about implicit int, because I've got lots and lots of old code that I don't feel like rewriting.)

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

#4


1  

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

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

Because in C, but not in C++, a function without a prototype is assumed to return an int.

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

This is an implicit declaration of that function. If that assumption turns out to be true (the function is declared later on with return-type of int), then the program compiles just fine.

这是该函数的隐式声明。如果该假设结果为真(函数稍后使用返回类型int声明),那么程序编译就好了。

If that assumption turns out to be false (it was assumed to return an int, but then actually is found to return a double, for example) then you get a compiler error that two functions cannot have the same name. (eg. int foo() and double foo() cannot both exist in the same program)

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

Note that all of this is C only.
In C++, implicit declarations are not allowed. Even if they were, the error message would be different because C++ has function overloading. The error would say that overloads of a function cannot differ only by return type. (overloading happens in the parameter list, not the return-type)

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

#1


11  

The fact that the code "compiles" as a program doesn't mean you can do it. The compiler should warn about implicit declaration of the function foo().

代码“编译”为c程序的事实并不意味着你可以做到。编译器应该警告函数foo()的隐式声明。

In this particular case implicit declaration would declare an identical foo() and nothing bad will happen.

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

But suppose the following, say this is

但假设有以下情况,请说明这一点

main.c

/* Don't include any header, why would you include it if you don't
   need prototypes? */

int main(void)
{
    printf("%d\n", foo()); // Use "%d" because the compiler will
                           // implicitly declare `foo()` as
                           //
                           //              int foo()
                           //
                           // Using the "correct" specifier, would
                           // invoke undefined behavior "too".
    return 0;
}

Now suppose foo() was defined in a different compilation unit1foo.c as

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

foo.c

double foo()
{
    return 3.5;
}

does it work as expected?

它是否按预期工作?

You could imagine what would happen if you use malloc() without including stdio.h, which is pretty much the same situation I try to explain above.

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

So doing this will invoke undefined behavior2, thus the term "Works" is not applicable in the understandable sense in this situation.

因此,执行此操作将调用未定义的行为2,因此术语“Works”在这种情况下不适用于可理解的意义。

The reason this could compile is because in the very old days it was allowed by the standard, namely the standard.

这可以编译的原因是因为在过去很久以前它被c标准所允许,即c89标准。

The standard has never allowed this so you can't compile a program if you call a function that has no prototype ("declaration") in the code before it's called.

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

Modern compilers warn about this because of the potential for undefined behavior that can easily occur, and since it's not that hard to forget to add a prototype or to include the appropriate header it's better for the programmer if the compiler can warn about this instead of suddenly having a very unexplicable bug.

现代c编译器警告这一点,因为可能容易发生未定义的行为,并且由于忘记添加原型或包含适当的头并不难,如果编译器可以对此进行警告而不是突然间有一个非常难以理解的错误。


1It can't be compiled in the same file because it would be defined with a different return type, since it was already implicitly declared

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

2Starting with the fact that double and int are different types, there will be undefined behavior because of this.

2启动double和int是不同类型的事实,因此会有未定义的行为。

#2


5  

When C was developed, the function name was all you needed to be able to call it. Matching arguments to function parameters was strictly the business of the programmer; the compiler didn't care if you passed three floats to something that needed just an integer.

在开发C时,函数名称就是您需要能够调用它的全部名称。函数参数的匹配参数完全是程序员的业务;编译器并不关心你是否将三个浮点数传递给只需一个整数的东西。

However, that turned out to be rather error prone, so later iterations of the C language added function prototypes as a (still optional) additional restriction. In C++ these restrictions have been tightened further: now a function prototype is always mandatory.

然而,事实证明这很容易出错,因此后来C语言的迭代添加了函数原型作为(仍然是可选的)附加限制。在C ++中,这些限制已经进一步加强:现在函数原型总是强制性的。

We can speculate on why, but in part this is because in C++ it is no longer enough to simply know the function name. There can be multiple functions with the same name, but with different arguments, and the compiler must figure out which one to call. It also has to figure out how to call (direct or virtual?), and it may even have to generate code in case of a template function.

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

In light of all that I think it makes sense to have the language demand that the function prototype be known at the point where the function is called.

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

#3


2  

Originally, C had no function prototypes, and C++ did not exist.

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

If you said

如果你说

extern double atof();

this said that atof was a function returning double. (Nothing was said about its arguments.)

这说是atof是一个返回双重的函数。 (关于其论点没有任何说法。)

If you then said

如果你那么说

double d = atof("1.3");

it would work. If you said

它会奏效。如果你说

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

the compiler would not complain, but something weird would happen if you tried to run it.

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

In those days, if you wanted to catch errors related to calling functions with the wrong number of arguments, that was the job of a separate program, lint, not the C compiler.

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

Also in those days, if you just called a function out of the blue that the compiler had never heard of before, like this:

同样在那些日子里,如果你刚刚调用了编译器之前从未听说过的函数,就像这样:

int i = atoi("42");

the compiler basically pretended that earlier you had said

编译器基本上假装你早先说过

extern int atoi();

This was what was called an implicit function declaration. Whenever the compiler saw a call to a function whose name it didn't know, the compiler assumed it was a function returning int.

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

Fast forward a few years. C++ invents the function prototypes that we know today. Among other things, they let you declare the number and types of the arguments expected by a function, not just its return type.

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

Fast forward a few more years, C adopts function prototypes, optionally. You can use them if you want, but if you don't, the compiler will still do an implicit declaration on any unknown function call it sees.

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

Fast forward a few more years, to C11. Now implicit int is finally gone. A compiler is required to complain if you call a function without declaring it first.

快进几年,到C11。现在隐式int终于消失了。如果在没有先声明函数的情况下调用函数,则需要编译器进行投诉。

But even today, you may be using a pre-C11 compiler that's still happy with implicit int. And a C11-complaint compiler may only issue a warning (not a compilation-killing error) if you forget to declare a function before calling it. And a C11-compliant compiler may offer an option to turn off those warnings, and quietly accept implicit ints. (For example, when I use the very modern clang, I arrange to invoke it with -Wno-implicit-int, meaning that I don't want warnings about implicit int, because I've got lots and lots of old code that I don't feel like rewriting.)

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

#4


1  

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

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

Because in C, but not in C++, a function without a prototype is assumed to return an int.

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

This is an implicit declaration of that function. If that assumption turns out to be true (the function is declared later on with return-type of int), then the program compiles just fine.

这是该函数的隐式声明。如果该假设结果为真(函数稍后使用返回类型int声明),那么程序编译就好了。

If that assumption turns out to be false (it was assumed to return an int, but then actually is found to return a double, for example) then you get a compiler error that two functions cannot have the same name. (eg. int foo() and double foo() cannot both exist in the same program)

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

Note that all of this is C only.
In C++, implicit declarations are not allowed. Even if they were, the error message would be different because C++ has function overloading. The error would say that overloads of a function cannot differ only by return type. (overloading happens in the parameter list, not the return-type)

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