一劳永逸地理解 C 和 C++ 中 f() 和 f(void) 之间的区别
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13319492/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Understanding the difference between f() and f(void) in C and C++ once and for all
提问by Dietrich Epp
Ok, so I have heard different opinions on this subject and just want to make sure I understand it correctly.
好的,所以我听到了关于这个主题的不同意见,只是想确保我正确理解它。
For C++
对于 C++
Declarations void f();
and void f(void);
mean precisely the same thing, the function f
does not take any parameters. Ditto for definitions.
声明void f();
和void f(void);
意思完全一样,函数f
不带任何参数。定义同上。
For C
对于 C
Declaration void f(void);
means that f
does not take any parameters.
声明void f(void);
意味着f
不带任何参数。
Declaration void f();
means that function f
may or may not have parameters, and if it does, we don't know what kind of parameters those are, or how many there is of them. Note that it is NOT the same as ellipsis, we can't use va_list
.
声明void f();
意味着函数f
可能有也可能没有参数,如果有,我们不知道这些是什么类型的参数,或者有多少参数。请注意,它与省略号不同,我们不能使用va_list
.
Now here is where things get interesting.
现在事情变得有趣了。
Case 1
情况1
Declaration:
宣言:
void f();
Definition:
定义:
void f(int a, int b, float c)
{
//...
}
Case 2
案例二
Declaration:
宣言:
void f();
Definition:
定义:
void f()
{
//...
}
Question:
题:
What happens at compile time in cases 1 and 2 when we call f
with the correct arguments, wrong arguments and no arguments at all? What happens at run time?
在情况 1 和 2 中,当我们f
使用正确的参数、错误的参数和根本没有参数调用时,在编译时会发生什么?运行时会发生什么?
Additional question:
补充问题:
If I declare f
with arguments, but define it without them, will it make a difference? Should I be able to address the arguments from the function body?
如果我f
用参数声明,但没有参数定义它,会有所不同吗?我应该能够解决函数体中的参数吗?
回答by Dietrich Epp
More terminology (C, not C++): a prototype for a function declares the types of its arguments. Otherwise the function does not have a prototype.
更多术语(C,而不是 C++):函数的原型声明其参数的类型。否则函数没有原型。
void f(); // Declaration, but not a prototype
void f(void); // Declaration and prototype
void f(int a, int b, float c); // Declaration and prototype
Declarations that aren't prototypes are holdovers from pre-ANSI C, from the days of K&R C. The only reason to use an old-style declaration is to maintain binary compatibility with old code. For example, in Gtk 2 there is a function declaration without a prototype -- it is there by accident, but it can't be removed without breaking binaries. The C99 standard comments:
不是原型的声明是从 K&R C 时代开始的 ANSI C 之前的保留。使用旧式声明的唯一原因是保持与旧代码的二进制兼容性。例如,在 Gtk 2 中有一个没有原型的函数声明——它是偶然出现的,但是如果不破坏二进制文件就不能删除它。C99标准评论:
6.11.6 Function declarators
The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.
6.11.6 函数声明符
使用带空括号的函数声明符(不是原型格式参数类型声明符)是一个过时的特性。
Recommendation:I suggest compiling all C code in GCC/Clang with -Wstrict-prototypes
and -Wmissing-prototypes
, in addition to the usual -Wall -Wextra
.
建议:我建议在编译GCC / Clang的所有C代码-Wstrict-prototypes
和-Wmissing-prototypes
,除了通常的-Wall -Wextra
。
What happens
发生什么了
void f(); // declaration
void f(int a, int b, float c) { } // ERROR
The declaration disagrees with the function body! This is actually a compile timeerror, and it's because you can't have a float
argument in a function without a prototype. The reason you can't use a float
in an unprototyped function is because when you call such a function, all of the arguments get promoted using certain default promotions. Here's a fixed example:
声明与函数体不一致!这实际上是一个编译时错误,这是因为float
在没有原型的函数中不能有参数。您不能float
在非原型函数中使用 a 的原因是,当您调用这样的函数时,所有参数都会使用某些默认提升进行提升。这是一个固定的例子:
void f();
void g()
{
char a;
int b;
float c;
f(a, b, c);
}
In this program, a
is promoted to int
1and c
is promoted to double
. So the definition for f()
has to be:
在这个程序中,a
提升为int
1并c
提升为double
。所以对于的定义f()
必须是:
void f(int a, int b, double c)
{
...
}
See C99 6.7.6 paragraph 15,
见 C99 6.7.6 第 15 段,
If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions.
如果一种类型具有参数类型列表,而另一种类型由不属于函数定义的一部分且包含空标识符列表的函数声明符指定,则参数列表不应有省略号终止符,并且每个参数的类型应与应用默认参数提升所产生的类型兼容。
Answer 1
答案 1
What happens at compile time in cases 1 and 2 when we call
f
with the correct arguments, wrong arguments and no arguments at all? What happens at run time?
在情况 1 和 2 中,当我们
f
使用正确的参数、错误的参数和根本没有参数调用时,在编译时会发生什么?运行时会发生什么?
When you call f()
, the parameters get promoted using the default promotions. If the promoted types match the actual parameter types for f()
, then all is good. If they don't match, it will probablycompile but you will definitely get undefined behavior.
当您调用 时f()
,参数将使用默认提升进行提升。如果提升的类型与 的实际参数类型匹配f()
,那么一切都很好。如果它们不匹配,它可能会编译,但你肯定会得到未定义的行为。
"Undefined behavior" is spec-speak for "we make no guarantees about what will happen." Maybe your program will crash, maybe it will work fine, maybe it will invite your in-laws over for dinner.
“未定义的行为”是“我们不保证会发生什么”的规范。也许你的程序会崩溃,也许它会运行良好,也许它会邀请你的姻亲过来吃晚饭。
There are two ways to get diagnostics at compile-time. If you have a sophisticated compiler with cross-module static analysis capabilities, then you will probably get an error message. You can also get messages for un-prototyped function declarations with GCC, using -Wstrict-prototypes
-- which I recommend turning on in all your projects (except for files which use Gtk 2).
有两种方法可以在编译时获得诊断信息。如果您有一个具有跨模块静态分析功能的复杂编译器,那么您可能会收到一条错误消息。您还可以使用 GCC 获取非原型函数声明的消息,使用-Wstrict-prototypes
-- 我建议在您的所有项目中打开它(使用 Gtk 2 的文件除外)。
Answer 2
答案 2
If I declare
f
with arguments, but define it without them, will it make a difference? Should I be able to address the arguments from the function body?
如果我
f
用参数声明,但没有参数定义它,会有所不同吗?我应该能够解决函数体中的参数吗?
It shouldn't compile.
它不应该编译。
Exceptions
例外
There are actually two cases in which function arguments are allowed to disagree with the function definition.
实际上有两种情况允许函数参数与函数定义不一致。
It is okay to pass
char *
to a function that expectsvoid *
, and vice versa.It is okay to pass a signed integer type to a function that expects the unsigned version of that type, or vice versa, as long as the value is representable in both types (i.e., it is not negative, and not out of range of the signed type).
传递
char *
给期望 的函数是可以的void *
,反之亦然。可以将有符号整数类型传递给需要该类型的无符号版本的函数,反之亦然,只要该值在两种类型中都可以表示(即,它不是负数,并且不超出范围签名类型)。
Footnotes
脚注
1: It is possiblethat char
promotes to unsigned int
, but this is very uncommon.
1:这是有可能的是char
提升到unsigned int
,但是这是非常罕见的。
回答by paxdiablo
The whole thing is really a moot point if you're using C99 or later (and, unless you're stuck on an old embedded system or something like that, you probably shouldbe using something more modern).
如果您使用的是 C99 或更高版本(并且,除非您被困在旧的嵌入式系统或类似系统上,否则您可能应该使用更现代的东西),整个事情确实是一个有争议的问题。
C99/C11 section 6.11.6 Future language directions, Function declarators
states:
C99/C11 部分6.11.6 Future language directions, Function declarators
规定:
The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.
使用带空括号的函数声明符(不是原型格式参数类型声明符)是一个过时的特性。
Hence you should avoid using things like void f();
altogether.
因此,您应该避免void f();
完全使用类似的东西。
If it takes parameters, list them, forming a proper prototype. If not, us void
to indicate definitively that it doesn't take any parameters.
如果需要参数,列出它们,形成一个合适的原型。如果没有,我们void
明确表示它不带任何参数。
回答by rs2012
In C++, f() and f(void) is same
在 C++ 中,f() 和 f(void) 是相同的
In C, they are different and any number of arguments can be passed while calling f() function but no argument can be passed in f(void)
在 C 中,它们是不同的,调用 f() 函数时可以传递任意数量的参数,但 f(void) 中不能传递参数
回答by Software_Designer
In pure C, this results in the error: error C2084: function 'void __cdecl f(void )' already has a body
在纯 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
But in Pure C++, it will compile without errors.
但是在纯 C++ 中,它会编译而不会出错。
Overloading functions (C++ only, C has no overloading)
重载函数(仅限 C++,C 没有重载)
You overload a function name f by declaring more than one function with the name f in the same scope. The declarations of f must differ from each other by the types and/or the number of arguments in the argument list. When you call an overloaded function named f, the correct function is selected by comparing the argument list of the function call with the parameter list of each of the overloaded candidate functions with the name f.
您可以通过在同一作用域中声明多个名称为 f 的函数来重载名称为 f 的函数。f 的声明必须因参数列表中的参数类型和/或参数数量而彼此不同。当您调用名为 f 的重载函数时,通过将函数调用的参数列表与每个名为 f 的重载候选函数的参数列表进行比较来选择正确的函数。
example:
例子:
#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;
}
output:
输出:
Here is int 10
Here is float 10.1
Here is char* ten