简体   繁体   English

如果在 main() 之后定义的函数中没有返回值,为什么 C 中不需要函数原型?

[英]Why is a function prototype not needed in C if there is no return in a function defined after main()?

As expected following code generates an error if prototype double cubenum();正如预期的那样,如果原型double cubenum(); is not declared as required in C.没有按照 C 中的要求进行声明。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("Answer is: %f", cubenum(3.0));
    return 0;
}

double cubenum(double number){
    double result = number * number * number;
    return result;
}

Whereas if cubenum definition is above is replaced with following definition without return then it does not generate any error when cubenum prototype is not declared:而如果上面的cubenum定义被下面的定义替换而不返回,那么当cubenum原型未声明时它不会产生任何错误:

void cubenum(double number){
    double result = number * number * number;
    printf("Answer is: %f", result);
}

And when prototype is declared as void cubenum();当原型被声明为 void cubenum(); with above cubenum definition without return it generates following error:上面的cubenum定义没有返回它会产生以下错误:

||=== Build: Debug in xxx(compiler: GNU GCC Compiler) ===|
C:\xxx\main.c||In function 'main':|
C:\xxx\main.c|10|error: invalid use of void expression|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

Line 10 was when tested: printf("Answer is: %f", cubenum(3.0));第 10 行是在测试时: printf("Answer is: %f", cubenum(3.0));

So, question is:所以,问题是:

Why a function which does not have a return, prototype declaration is not required and if declared gives error in the above example?为什么一个没有返回值的函数,不需要原型声明,如果在上面的例子中声明会出错?

GCC version info GCC 版本信息

gcc (MinGW.org GCC-6.3.0-1) 6.3.0

You have written your program in a way that is characteristic of very old C programs, dating to the 1980s or 1990s, before "prototyped" function declarations became preferred style.在“原型化”函数声明成为首选风格之前,您以非常古老的C 程序的特征编写程序,可追溯到 1980 年代或 1990 年代。 C compilers, to this day, bend over backwards to keep those very old programs working, preserving language features that they rely on, but that were never standardized or have been removed from the C standard since 1989.时至今日,C 编译器一直在努力让那些非常古老的程序继续工作,保留它们所依赖的语言特性,但这些特性从未标准化或自 1989 年以来已从 C 标准中删除。

Perfectly correct modern style for your first program would look like this:您的第一个程序的完美现代风格将如下所示:

#include <stdio.h>

double cubenum(double);

int main(void)
{
    printf("Answer is: %f", cubenum(3.0));
    return 0;
}

double cubenum(double number)
{
    double result = number * number * number;
    return result;
}

with a prototyped forward declaration of cubenum , double cubenum(double);带有一个原型cubenum前向声明, double cubenum(double);

It's important to understand that double cubenum();了解double cubenum();很重要double cubenum(); is NOT a prototyped declaration in C, but rather a declaration that says cubenum takes any number and type of arguments.不是 C 中的原型声明,而是声明cubenum接受任意数量和类型的参数。 If you wanted to specify that cubenum takes no arguments, you would have to write double cubenum(void);如果要指定cubenum任何参数,则必须编写double cubenum(void); This is also why I changed int main() to int main(void) .这也是我将int main()更改为int main(void)

When you leave the forward declaration out entirely,当你完全离开前向声明时,

#include <stdio.h>

int main(void)
{
    printf("Answer is: %f", cubenum(3.0));
    return 0;
}

double cubenum(double number)
{
    double result = number * number * number;
    return result;
}

the C compiler sees a call to cubenum with no previous declaration for it at all. C 编译器看到了一个对cubenum的调用,而之前根本没有对它进行声明。 This was common in those very old C programs I mentioned.这在我提到的那些非常古老的 C 程序中很常见。 They relied on a feature called implicit declaration that was part of the original C standard but removed from its 1999 revision (commonly known as "C99").他们依赖于称为隐式声明的特性,该特性是原始 C 标准的一部分,但已从其 1999 年修订版(通常称为“C99”)中删除。 Basically, the compiler assumes that the programmer meant to write int cubenum();基本上,编译器假定程序员打算编写int cubenum(); above main , but lazily left it out.main之上,但懒惰地将其排除在外。 This means cubenum takes any number and type of arguments (NOT that it takes no arguments) and returns int .这意味着cubenum接受任意数量和类型的参数(不是它不接受任何参数)并返回int So, leaving main out of it for now, it's like you wrote所以,暂时把main放在外面,就像你写的

int cubenum();
double cubenum(double number) { ... }

and the compiler rejects the program because the definition of cubenum has a different return type from the (implicit) forward declaration.并且编译器拒绝该程序,因为cubenum的定义与(隐式)前向声明具有不同的返回类型。 That part I think you already understood.那部分我想你已经明白了。

Now, when you change cubenum to return nothing, so your complete program is现在,当你改变cubenum不返回任何东西时,你的完整程序是

#include <stdio.h>

int main(void)
{
    printf("Answer is: %f", cubenum(3.0));
    return 0;
}

void cubenum(double number)
{
    double result = number * number * number;
    printf("Answer is: %f", result);
}

the implicit function declaration is still int cubenum() and the prototype from the function definition is void cubenum(double) .隐式函数声明仍然是int cubenum()而函数定义的原型是void cubenum(double) As another compatibility feature for those very old C programs, these are considered not to be conflicting return types, and the compiler accepts the program.作为那些非常旧的 C 程序的另一个兼容性特性,这些被认为是冲突的返回类型,并且编译器接受该程序。 This is because the type void was invented in the 1989 C standard.这是因为类型void是在 1989 C 标准中发明的。 Programs written before then would have instead given cubenum no return type at all...在此之前编写的程序根本不会给cubenum任何返回类型......

cubenum(number)
    double number;
{
    double result = number * number * number;
    printf("Answer is: %f", result);
}

... which technically declares it to return int ! ...技术上声明它返回int Immediately post-C89, those programs got updated to give their functions with no return value the type void , but it was too much work to stop relying on implicit declarations for them at the same time, so compilers grew a special case where int foo() and void foo() are considered not to be in conflict.在 C89 之后,这些程序立即得到更新,为它们的没有返回值的函数提供void类型,但是同时停止依赖它们的隐式声明工作量太大,因此编译器增加了一个特殊情况,即int foo()void foo()被认为不冲突。

(Incidentally, because of yet another backward compatibility consideration — "old-style function definitions", which you can see in the above code fragment — preferred style in C is to put the opening curly brace of a function definition on its own line, even if all other opening braces are "cuddled".) (顺便说一下,由于另一个向后兼容性考虑——“旧式函数定义”,你可以在上面的代码片段中看到——C 中的首选风格是将函数定义的左花括号放在自己的行上,即使如果所有其他左括号都被“拥抱”。)

And finally, when you do put void cubenum();最后,当void cubenum(); above main , only then is the compiler officially aware that cubenum returns nothing.main之上,编译器才正式意识到cubenum什么都不返回。 When it does know that, it knows that printf("%f", cubenum(3.0));当它知道时,它知道printf("%f", cubenum(3.0)); is incorrect because it's using the nonexistent return value of cubenum , and it rejects the program for that reason.不正确,因为它使用的不存在的返回值cubenum ,并拒绝了这个原因的程序。


You shouldn't be relying on any of these backward compatibility features in a new program.您不应该在新程序中依赖任何这些向后兼容性功能。 I see that you are using GCC, so set your compilation options to something like this:我看到您正在使用 GCC,因此将您的编译选项设置为如下所示:

-std=gnu11 -g -Og -Wall -Wpedantic -Wstrict-prototypes -Wold-style-definition -Werror

which will disable almost all of the backward compatibility features.这将禁用几乎所有的向后兼容性功能。 (There are a whole lot more warning options and you may want to consider turning on more of them. -Wwrite-strings and -Wextra are particularly useful for new code IMNSHO.) (Do NOT use the hyper-conformant mode, -std=c11 , until you know considerably more about what you are doing; it can break the system headers, and it enables the "trigraph" misfeature that you almost certainly don't want.) (有更多警告选项,您可能需要考虑打开更多选项-Wwrite-strings-Wextra对新代码 IMNSHO 特别有用。)(不要使用超符合模式, -std=c11 ,直到您对自己在做什么有了更多的了解;它会破坏系统标头,并且会启用您几乎肯定不想要的“trigraph”错误功能。)

Whereas if cubenum definition is above is replaced with following definition without return then it does not generate any error when cubenum prototype is not declared:而如果上面的cubenum定义被下面的定义替换而不返回,那么当cubenum原型未声明时它不会产生任何错误:

Because when you don't have explicit declaration for a function, some C compilers will do it implicitly.因为当您没有对函数进行显式声明时,某些 C 编译器会隐式声明。 Normally, it gives int for both return type and arguments.通常,它为返回类型和参数提供int

A good-enough compiler should inform you about implicit declaration.一个足够好的编译器应该告诉你隐式声明。

It's dangerous, because compiler won't know the exact prototype of the function.So you may have a very unpredictable result.这是危险的,因为编译器不会知道函数的确切原型。所以你可能会得到一个非常不可预测的结果。

And when prototype is declared as void cubenum();当原型被声明为 void cubenum(); with above cubenum definition without return it generates following error:上面的cubenum定义没有返回它会产生以下错误:

This is another problem, since you try to printf the return value of a void function.这是另一个问题,因为您尝试printf void函数的返回值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM