double cubenum();正如预期的那样,如果原型未按 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;
}
Run Code Online (Sandbox Code Playgroud)
然而,如果将上面的cubenum定义替换为以下定义而不返回,那么当未声明cubenum原型时,它不会生成任何错误:
void cubenum(double number){
double result = number * number * number;
printf("Answer is: %f", result);
}
Run Code Online (Sandbox Code Playgroud)
当原型声明为 voidcubumum(); 时 使用上面的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)) ===|
Run Code Online (Sandbox Code Playgroud)
测试时第10行是: printf("Answer is: %f", cubenum(3.0));
所以,问题是:
为什么没有返回值的函数不需要原型声明,并且如果声明会在上面的示例中给出错误?
海湾合作委员会版本信息
gcc (MinGW.org GCC-6.3.0-1) 6.3.0
Run Code Online (Sandbox Code Playgroud)
您编写的程序具有非常古老的C 程序的特征,可以追溯到 20 世纪 80 年代或 90 年代,当时“原型”函数声明成为首选风格。时至今日,C 编译器仍在竭尽全力让那些非常古老的程序继续运行,保留它们所依赖的语言功能,但这些功能从未标准化或自 1989 年以来已从 C 标准中删除。
\n\n您的第一个程序的完美正确的现代风格将如下所示:
\n\n#include <stdio.h>\n\ndouble cubenum(double);\n\nint main(void)\n{\n printf("Answer is: %f", cubenum(3.0));\n return 0;\n}\n\ndouble cubenum(double number)\n{\n double result = number * number * number;\n return result;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n带有原型的前向声明cubenum,double cubenum(double);
重要的是要理解这double cubenum();不是 C 中的原型声明,而是一个cubenum接受任意数量和类型参数的声明。如果您想指定不cubenum带任何参数,则必须编写 double cubenum(void);这也是我更改int main()为的原因int main(void)。
当您完全省略前向声明时,
\n\n#include <stdio.h>\n\nint main(void)\n{\n printf("Answer is: %f", cubenum(3.0));\n return 0;\n}\n\ndouble cubenum(double number)\n{\n double result = number * number * number;\n return result;\n}\nRun Code Online (Sandbox Code Playgroud)\n\nC 编译器会看到对 的调用cubenum,而之前根本没有声明它。这在我提到的那些非常古老的 C 程序中很常见。他们依赖于一种称为隐式声明的功能,该功能是原始 C 标准的一部分,但从 1999 年修订版(通常称为“C99”)中删除。基本上,编译器假设程序员本来想写int cubenum();上面的main内容,但懒惰地将其遗漏了。这意味着cubenum接受任意数量和类型的参数(而不是不接受参数)并返回int。所以,main暂时不考虑它,就像你写的
int cubenum();\ndouble cubenum(double number) { ... }\nRun Code Online (Sandbox Code Playgroud)\n\n并且编译器会拒绝该程序,因为 的定义与cubenum(隐式)前向声明具有不同的返回类型。这部分我想你已经明白了。
现在,当您更改cubenum为不返回任何内容时,因此您的完整程序是
#include <stdio.h>\n\nint main(void)\n{\n printf("Answer is: %f", cubenum(3.0));\n return 0;\n}\n\nvoid cubenum(double number)\n{\n double result = number * number * number;\n printf("Answer is: %f", result);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n隐式函数声明仍然是int cubenum(),函数定义的原型是void cubenum(double)。作为那些非常古老的 C 程序的另一个兼容性功能,这些被认为是不冲突的返回类型,并且编译器接受该程序。这是因为该类型void是在 1989 年 C 标准中发明的。在此之前编写的程序将根本cubenum不给出返回类型......
cubenum(number)\n double number;\n{\n double result = number * number * number;\n printf("Answer is: %f", result);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n...从技术上来说,它宣告了它的回归int!C89 之后,这些程序立即得到更新,为没有返回值的函数提供 type void,但是同时停止依赖它们的隐式声明需要太多工作,因此编译器产生了一种特殊情况,其中int foo()和void foo()被认为不是发生冲突。
(顺便说一句,由于另一个向后兼容性的考虑 \xe2\x80\x94 “旧式函数定义”,您可以在上面的代码片段中看到 \xe2\x80\x94 C 中的首选样式是将左大括号放在函数定义在其自己的行上,即使所有其他左大括号都是“拥抱”的。)
\n\n最后,当您确实将其放在void cubenum();上面时main,只有这样编译器才正式意识到cubenum不返回任何内容。当它确实知道这一点时,它就知道这printf("%f", cubenum(3.0));是不正确的,因为它使用的是不存在的返回值cubenum,并且因此拒绝该程序。
您不应该依赖新程序中的任何向后兼容性功能。我发现您正在使用 GCC,因此将编译选项设置为如下所示:
\n\n-std=gnu11 -g -Og -Wall -Wpedantic -Wstrict-prototypes -Wold-style-definition -Werror\nRun Code Online (Sandbox Code Playgroud)\n\n这将禁用几乎所有向后兼容功能。(还有更多警告选项,您可能需要考虑打开更多警告选项。 -Wwrite-strings并且-Wextra对于新代码特别有用,恕我直言。)(请勿使用超一致模式,-std=c11除非您对您要执行的操作有更多了解。正在做的事情;它可能会破坏系统标头,并且会启用您几乎肯定不想要的“三字母”错误功能。)