理解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
1 , c
提升为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
,但是如果没有它们来定义它,它会有所作为吗? 我应该能够解决来自函数体的争论吗?
它不应该编译。
例外
实际上有两种情况下函数参数可以不同意函数定义。
-
可以将
char *
传递给期望void *
的函数,反之亦然。 -
可以将一个有符号的整数types传递给一个需要该types的无符号版本的函数,反之亦然,只要这个值可以用两种types表示(即它不是负数,也不超出范围签名types)。
脚注
1 : char
可能会提升为unsigned int
,但是这是非常罕见的。
如果你使用的是C99或更高版本,那么整个事情确实是一个有争议的问题(除非你被困在一个旧的embedded式系统或类似的东西上,否则你可能应该使用更现代的东西)。
C99 / C11第6.11.6 Future language directions, Function declarators
节6.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