理解C和C ++中f()和f(void)之间的区别

可能重复:
在现代的C和C ++中已经弃用了f(void)

好的,所以我在这个问题上听到了不同的意见,只是想确保我理解正确。

对于C ++

声明void f();void f(void); 意思完全一样,函数f不带任何参数。 同上定义。

对于C.

声明void f(void); 意味着f不采取任何参数。

声明void f(); 意味着函数f可能有也可能没有参数,如果有的话,我们不知道它们是什么样的参数,或者它们有多less。 请注意,它与省略号不一样,我们不能使用va_list

现在,这是事情变得有趣的地方。

情况1

宣言:

 void f(); 

定义:

 void f(int a, int b, float c) { //... } 

案例2

宣言:

 void f(); 

定义:

 void f() { //... } 

题:

在第一种情况和第二种情况下,我们在正确的论点,错误的论据和没有论据的情况下调用f ,会发生什么? 运行时会发生什么?

附加问题:

如果我用参数来声明f ,但是如果没有它们来定义它,它会有所作为吗? 我应该能够解决来自函数体的争论吗?

更多的术语(C,而不是C ++):函数的原型声明了它的参数的types。 否则,该function没有原型。

 void f(); // Declaration, but not a prototype void f(void); // Declaration and prototype void f(int a, int b, float c); // Declaration and prototype 

不是原型的声明是从K&R C以前的ANSI C以前的版本开始的。使用旧式声明的唯一原因是保持与旧代码的二进制兼容性。 例如,在Gtk 2中有一个没有原型的函数声明 – 这是偶然发生的,但不能在不破坏二进制文件的情况下被移除。 C99标准评论:

6.11.6函数声明符

具有空括号的函数声明符(不是原型格式参数types声明符)的使用是过时的function。

build议:我build议编写GCC / Clang中的所有C代码,除了通常的-Wall -Wextra外,还有-Wstrict-prototypes-Wmissing-prototypes

怎么了

 void f(); // declaration void f(int a, int b, float c) { } // ERROR 

声明不同意函数体! 这实际上是一个编译时错误,这是因为在没有原型的情况下,函数中不能有float参数。 你不能在一个非原型的函数中使用float的原因是因为当你调用这样一个函数时,所有的参数都会被使用某些默认的提升来提升。 这是一个固定的例子:

 void f(); void g() { char a; int b; float c; f(a, b, c); } 

在这个程序中, a被提升为int 1c提升为double 。 所以f()的定义必须是:

 void f(int a, int b, double c) { ... } 

见C99 6.7.6第15段,

如果一个types有一个参数types列表,而另一个types是由一个函数声明符指定的,而该函数声明符不是一个函数定义的一部分,并且包含一个空的标识符列表,则参数列表中不应该有省略号结束符,每个参数的types应该与应用默认参数促销的结果types兼容。

答案1

在第一种情况和第二种情况下,我们在正确的论点,错误的论据和没有论据的情况下调用f ,会发生什么? 运行时会发生什么?

当您调用f() ,将使用默认的促销活动来提升参数。 如果升级的types匹配f()的实际参数types,则一切正常。 如果它们不匹配,它可能会编译,但你一定会得到未定义的行为。

“未定义的行为”就是“我们对将要发生的事情没有保证”。 也许你的程序会崩溃,也许它会正常工作,也许会邀请你的公婆吃晚饭。

有两种方法可以在编译时进行诊断。 如果您有一个具有跨模块静态分析function的复杂编译器,那么您可能会收到错误消息。 你也可以使用-Wstrict-prototypes (我build议在你所有的项目中使用的除外(使用Gtk 2的文件除外))来-Wstrict-prototypes使用GCC的未原型函数声明的消息。

答案2

如果我用参数来声明f ,但是如果没有它们来定义它,它会有所作为吗? 我应该能够解决来自函数体的争论吗?

它不应该编译。

例外

实际上有两种情况下函数参数可以不同意函数定义。

  1. 可以将char *传递给期望void *的函数,反之亦然。

  2. 可以将一个有符号的整数types传递给一个需要该types的无符号版本的函数,反之亦然,只要这个值可以用两种types表示(即它不是负数,也不超出范围签名types)。

脚注

1char 可能会提升为unsigned int ,但是这是非常罕见的。

如果你使用的是C99或更高版本,那么整个事情确实是一个有争议的问题(除非你被困在一个旧的embedded式系统或类似的东西上,否则你可能应该使用更现代的东西)。

C99 / C11第6.11.6 Future language directions, Function declarators6.11.6 Future language directions, Function declarators指出:

具有空括号的函数声明符(不是原型格式参数types声明符)的使用是过时的function。

因此,你应该避免使用像void f(); 共。

如果需要参数,列出它们,形成一个适当的原型。 如果不是的话,我们void地表明它没有任何参数。

在C ++中,f()和f(void)是相同的

在C中,它们是不同的,调用f()函数时可传递任意数量的参数,但f(void)中不能传递参数

在纯粹的C,这会导致错误: error C2084: function 'void __cdecl f(void )' already has a body

 void f(void); void f( ); int main() { f(10); f(10.10); f("ten"); return 0; } void f(void) { } void f( ) { } 

  fvoid.c line(19) : error C2084: function 'void __cdecl f(void )' already has a body 

但是在Pure C ++中,它会编译没有错误。

重载函数(只有C ++,C没有重载)

您通过在同一范围内声明多个名称为f的函数来重载函数名f。 f的声明必须根据参数列表中参数的types和/或数量而不同。 当调用名为f的重载函数时,通过比较函数调用的参数列表和每个重载候选函数的参数列表来select正确的函数,其名称为f。

例:

 #include <iostream> using namespace std; void f(int i); void f(double f); void f(char* c); int main() { f(10); f(10.10); f("ten"); return 0; } void f(int i) { cout << " Here is int " << i << endl; } void f(double f) { cout << " Here is float " << f << endl; } void f(char* c) { cout << " Here is char* " << c << endl; } 

输出:

  Here is int 10 Here is float 10.1 Here is char* ten