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

huangapple go评论65阅读模式
英文:

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

问题

以下是您提供的代码的翻译部分:

如预期,如果在C中未声明原型double cubenum();,则以下代码将生成错误。

#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;
}

而如果将上述的cubenum定义替换为以下没有返回值的定义,那么当未声明cubenum原型时,它不会生成任何错误:

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

当将原型声明为void cubenum(); 与上述没有返回值的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)) ===|

第10行出错是因为测试时执行了以下代码:printf("Answer is: %f", cubenum(3.0));

所以问题是:

为什么在上面的示例中,一个没有返回值的函数,如果没有声明原型,就不需要原型声明,但如果声明了原型,就会出现错误?

GCC版本信息:

gcc (MinGW.org GCC-6.3.0-1) 6.3.0
英文:

As expected following code generates an error if prototype double cubenum(); is not declared as required in C.

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

int main()
{
    printf(&quot;Answer is: %f&quot;, 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:

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

And when prototype is declared as void cubenum(); with above cubenum definition without return it generates following error:

||=== Build: Debug in xxx(compiler: GNU GCC Compiler) ===|
C:\xxx\main.c||In function &#39;main&#39;:|
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(&quot;Answer is: %f&quot;, 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 (MinGW.org GCC-6.3.0-1) 6.3.0

答案1

得分: 5

你已经以非常古老的C程序的方式编写了你的程序,这种方式可以追溯到20世纪80年代或90年代,这是在“原型化”函数声明成为首选风格之前。C编译器一直在尽力维护这些非常古老的程序,保留它们依赖但从未标准化或自1989年以来从C标准中删除的语言特性。

你的第一个程序的完全正确的现代样式应该如下所示:

#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;
}

其中有一个带有“prototyped”前向声明的 cubenumdouble cubenum(double);

重要的是要理解,double cubenum(); 在C中不是一个带原型的声明,而是一个声明,表示 cubenum 接受_任何数量和类型_的参数。如果你想要指定 cubenum 不接受任何参数,你需要写成 double cubenum(void); 这也是为什么我将 int main() 更改为 int main(void)

当你完全省略前向声明时:

#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;
}

C编译器会看到对 cubenum 的调用,但之前没有任何声明。这在我提到的那些非常古老的C程序中很常见。它们依赖于一个称为“隐式声明”的功能,它是原始C标准的一部分,但在1999年的修订版(通常称为“C99”)中删除了。基本上,编译器会假设程序员想要在 main 上面写 int cubenum();,但懒惰地省略了它。这意味着 cubenum 接受_任何数量和类型_的参数(而不是不接受参数),并返回 int。所以,暂时不考虑 main,这就像你写了

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

编译器会拒绝这个程序,因为 cubenum 的定义与(隐式的)前向声明的返回类型不同。我认为你已经理解了这一点。

现在,当你将 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);
}

隐式函数声明仍然是 int cubenum(),而来自函数定义的原型是 void cubenum(double)。作为对那些非常古老的C程序的兼容性特性,这被认为是_不_冲突的返回类型,编译器接受该程序。这是因为类型 void 是在1989年的C标准中引入的。在那之前编写的程序可能根本没有为它们的无返回值函数指定返回类型...

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

... 从技术上讲,它声明它返回 int!在C89后期,这些程序得到了更新,为它们的没有返回值的函数赋予了类型 void,但在同一时间停止依赖隐式声明太麻烦,所以编译器增加了一个特殊情况,其中 int foo()void foo() 被认为不冲突。

(顺便说一下,由于另一个向后兼容性考虑因素——“旧式函数定义”,你可以在上面的代码片段中看到,C中的首选风格是将函数定义的左大括号放在自己的一行上,即使所有其他的左大括号都是“拥抱”的。)

最后,当你在 main 上面加上 void cubenum(); 时,编译器才正式意识到 cubenum 不返回任何东西。当它知道这一点时,它知道 printf("Answer is: %f", cubenum(3.0)); 是不正确的,因为它使用了 cubenum 的不存在的返回值,因此出于这个原因拒绝了程序。


在新程序中,你不应该依赖任何这些向后兼容性特性。我看到你正在使用GCC,所以将你的编译选项设置为类似于以下内容:

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

这将禁用几乎所有的向后兼容性特性。 (还有更多的警告选项,你可能需要考虑打开更多的选项。-Wwrite-strings-Wextra 对新代码特别有用。)(在你完全了解你在做什么之前,不要使用超级一致的模式,-std=c11,它可能会破坏系统头文件,并启用你几乎肯定不需要的“三字符”功能。)

英文:

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 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.

Perfectly correct modern style for your first program would look like this:

#include &lt;stdio.h&gt;

double cubenum(double);

int main(void)
{
    printf(&quot;Answer is: %f&quot;, 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);

It's important to understand that double cubenum(); is NOT a prototyped declaration in C, but rather a declaration that says cubenum takes any number and type of arguments. If you wanted to specify that cubenum takes no arguments, you would have to write double cubenum(void); This is also why I changed int main() to int main(void).

When you leave the forward declaration out entirely,

#include &lt;stdio.h&gt;

int main(void)
{
    printf(&quot;Answer is: %f&quot;, 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. This was common in those very old C programs I mentioned. 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"). Basically, the compiler assumes that the programmer meant to write int cubenum(); above main, but lazily left it out. This means cubenum takes any number and type of arguments (NOT that it takes no arguments) and returns int. So, leaving main out of it for now, it's like you wrote

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. That part I think you already understood.

Now, when you change cubenum to return nothing, so your complete program is

#include &lt;stdio.h&gt;

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

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

the implicit function declaration is still int cubenum() and the prototype from the function definition is 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. This is because the type void was invented in the 1989 C standard. Programs written before then would have instead given cubenum no return type at all...

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

... which technically declares it to return 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.

(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".)

And finally, when you do put void cubenum(); above main, only then is the compiler officially aware that cubenum returns nothing. When it does know that, it knows that printf(&quot;%f&quot;, cubenum(3.0)); is incorrect because it's using the nonexistent return value of cubenum, and it rejects the program for that reason.


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:

-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.)

答案2

得分: 1

因为当你没有为一个函数显式声明时,一些C编译器会隐式地执行它。通常,它为返回类型和参数都给出int

一个足够好的编译器应该会提醒你关于隐式声明的问题。

这很危险,因为编译器不会知道函数的确切原型。所以你可能会得到一个非常不可预测的结果。

另一个问题是,当你尝试printf一个void函数的返回值时,会生成以下错误:

英文:

> 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:

Because when you don't have explicit declaration for a function, some C compilers will do it implicitly. Normally, it gives int for both return type and arguments.

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(); with above cubenum
> definition without return it generates following error:

This is another problem, since you try to printf the return value of a void function.

huangapple
  • 本文由 发表于 2020年1月6日 20:54:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/59612500.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定