使用 C void 参数“void foo(void)”是否更好? 或者不是“void foo()”?
void foo()
和 void foo(void)
哪个更好? 对于 void 它看起来丑陋且不一致,但有人告诉我它很好。 这是真的?
编辑:我知道一些旧的编译器会做一些奇怪的事情,但是如果我只使用 GCC,void foo()
可以吗? foo(bar);
会被接受吗?
What is better: void foo()
or void foo(void)
?
With void it looks ugly and inconsistent, but I've been told that it is good. Is this true?
Edit: I know some old compilers do weird things, but if I'm using just GCC, is void foo()
Ok? Will foo(bar);
then be accepted?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这是 C 语言中“无参数”的正确说法,在 C++ 中也适用。
但是:
在 C 和 C++ 中意味着不同的东西! 在 C 中,它的意思是“可以接受任意数量的未知类型的参数”,在 C++ 中,它的意思与 foo(void) 相同。
变量参数列表函数本质上是非类型安全的,应尽可能避免。
That is the correct way to say "no parameters" in C, and it also works in C++.
But:
Means different things in C and C++! In C it means "could take any number of parameters of unknown types", and in C++ it means the same as
foo(void)
.Variable argument list functions are inherently un-typesafe and should be avoided where possible.
C 中有两种指定参数的方法。一种是使用标识符列表,另一种是使用参数类型列表。 标识符列表可以省略,但类型列表不能。 因此,要说一个函数在函数定义中不带参数,您可以使用(省略的)标识符列表来实现这一点,
并且可以使用参数类型列表来实现:
如果在参数类型列表中唯一的一个参数类型是 void(它必须没有name then),那么这意味着该函数不带参数。 但这两种定义函数的方法在声明内容方面有所不同。
标识符列表
第一个定义函数采用特定数量的参数,但既不传达数量,也不传达所需内容的类型 - 与所有使用标识符列表的函数声明一样。 因此调用者必须事先准确地知道类型和计数。 因此,如果调用者调用该函数并为其提供一些参数,则该行为是未定义的。 例如,堆栈可能会被损坏,因为被调用的函数在获得控制权时需要不同的布局。
不推荐在函数参数中使用标识符列表。 它在过去就被使用过,并且仍然存在于许多生产代码中。 由于这些参数提升,它们可能会导致严重的危险(如果提升的参数类型与函数定义的参数类型不匹配,则行为也未定义!),并且当然安全性要低得多。 因此,在函数的唯一声明和定义中,始终对没有参数的函数使用
void
。参数类型列表
第二个定义该函数采用零个参数,并传达这一点 - 就像使用参数类型列表声明该函数的所有情况一样,该列表称为“原型”。 如果调用者调用该函数并为其提供一些参数,则这是一个错误,编译器会输出适当的错误。
声明函数的第二种方式有很多好处。 当然之一是检查参数的数量和类型。 另一个区别是,因为编译器知道参数类型,所以它可以将参数隐式转换为参数类型。 如果不存在参数类型列表,则无法完成此操作,并且参数将转换为升级类型(称为默认参数升级)。 例如,
char
将变为int
,而float
将变为double
。函数的复合类型
顺便说一句,如果文件同时包含省略的标识符列表和参数类型列表,则参数类型列表“获胜”。 最后的函数类型包含一个原型:
这是因为两个声明都没有说任何矛盾的内容。 然而,第二个还有一些话要说。 这就是一个论点被接受。 可以反向完成相同的
操作。第一个使用标识符列表定义函数,而第二个则使用包含参数类型列表的声明为其提供原型。
There are two ways for specifying parameters in C. One is using an identifier list, and the other is using a parameter type list. The identifier list can be omitted, but the type list can not. So, to say that one function takes no arguments in a function definition you do this with an (omitted) identifier list
And this with a parameter type list:
If in a parameter type list the only one parameter type is void (it must have no name then), then that means the function takes no arguments. But those two ways of defining a function have a difference regarding what they declare.
Identifier lists
The first defines that the function takes a specific number of arguments, but neither the count is communicated nor the types of what is needed - as with all function declarations that use identifier lists. So the caller has to know the types and the count precisely before-hand. So if the caller calls the function giving it some argument, the behavior is undefined. The stack could become corrupted for example, because the called function expects a different layout when it gains control.
Using identifier lists in function parameters is deprecated. It was used in old days and is still present in lots of production code. They can cause severe danger because of those argument promotions (if the promoted argument type do not match the parameter type of the function definition, behavior is undefined either!) and are much less safe, of course. So always use the
void
thingy for functions without parameters, in both only-declarations and definitions of functions.Parameter type list
The second one defines that the function takes zero arguments and also communicates that - like with all cases where the function is declared using a parameter type list, which is called a
prototype
. If the caller calls the function and gives it some argument, that is an error and the compiler spits out an appropriate error.The second way of declaring a function has plenty of benefits. One of course is that amount and types of parameters are checked. Another difference is that because the compiler knows the parameter types, it can apply implicit conversions of the arguments to the type of the parameters. If no parameter type list is present, that can't be done, and arguments are converted to promoted types (that is called the default argument promotion).
char
will becomeint
, for example, whilefloat
will becomedouble
.Composite type for functions
By the way, if a file contains both an omitted identifier list and a parameter type list, the parameter type list "wins". The type of the function at the end contains a prototype:
That is because both declarations do not say anything contradictory. The second, however, had something to say in addition. Which is that one argument is accepted. The same can be done in reverse
The first defines a function using an identifier list, while the second then provides a prototype for it, using a declaration containing a parameter type list.
void foo(void)
更好,因为它明确表示:不允许使用参数。void foo()
意味着您可以(在某些编译器下)发送参数,至少如果这是函数的声明而不是其定义。void foo(void)
is better because it explicitly says: no parameters allowed.void foo()
means you could (under some compilers) send parameters, at least if this is the declaration of your function rather than its definition.C99引用
本回答旨在引用并解释C99 N1256 标准草案。
声明符的定义
术语声明符会出现很多次,所以让我们来理解它。
从语言语法中,我们发现以下下划线字符是声明符:
声明符是函数声明和定义的一部分。
声明符有两种类型:
参数类型列表
声明如下:
定义如下:
称为参数类型列表,因为我们必须给出每个参数的类型。
标识符列表
定义如下:
声明如下:
我们不能声明具有非空标识符列表的函数:
因为 6.7.5.3“函数声明符(包括原型)” 说:
之所以称为标识符列表,是因为我们只在
f(x, y)
上给出标识符x
和y
,类型在后面。这是一种较旧的方法,不应再使用。 6.11.6 函数声明符 说:
简介解释了什么是过时的功能:
f() 与 f(void) 声明
当你只写:
它必然是一个标识符列表声明,因为6.7.5“声明符”说将语法定义为:
因此只有标识符列表版本可以为空,因为它是可选的(
_opt
)。direct-declarator
是唯一定义声明符的括号(...)
部分的语法节点。那么我们如何消除歧义并使用更好的无参数参数类型列表呢? 6.7.5.3 函数声明符(包括原型) 说:
所以:
就是这样。
这是明确允许的神奇语法,因为我们不能以任何其他方式使用
void
类型参数:如果我使用 f() 声明会发生什么?
也许代码将编译得很好:6.7.5.3 函数声明符(包括原型):
所以你可以逃脱:
其他时候,UB 可能会悄悄出现(如果你幸运的话,编译器会告诉你),你将很难找出原因:
请参阅:为什么空声明适用于带 int 参数的定义,但不适用于 float 参数?
f( ) 和 f(void) 的定义
vs
相似,但不相同。
6.7.5.3 函数声明符(包括原型) 说:
看起来与
f(void)
的描述类似。但仍然...似乎:
符合未定义的行为,而:
不符合以下讨论:为什么 gcc 允许将参数传递给定义为不带参数的函数?
TODO 明白为什么。 与是否是原型有关。 定义原型。
C99 quotes
This answer aims to quote and explain the relevant parts of the C99 N1256 standard draft.
Definition of declarator
The term declarator will come up a lot, so let's understand it.
From the language grammar, we find that the following underline characters are declarators:
Declarators are part of both function declarations and definitions.
There are 2 types of declarators:
Parameter type list
Declarations look like:
Definitions look like:
It is called parameter type list because we must give the type of each parameter.
Identifier list
Definitions look like:
Declarations look like:
We cannot declare a function with a non-empty identifier list:
because 6.7.5.3 "Function declarators (including prototypes)" says:
It is called identifier list because we only give the identifiers
x
andy
onf(x, y)
, types come after.This is an older method, and shouldn't be used anymore. 6.11.6 Function declarators says:
and the Introduction explains what is an obsolescent feature:
f() vs f(void) for declarations
When you write just:
it is necessarily an identifier list declaration, because 6.7.5 "Declarators" says defines the grammar as:
so only the identifier-list version can be empty because it is optional (
_opt
).direct-declarator
is the only grammar node that defines the parenthesis(...)
part of the declarator.So how do we disambiguate and use the better parameter type list without parameters? 6.7.5.3 Function declarators (including prototypes) says:
So:
is the way.
This is a magic syntax explicitly allowed, since we cannot use a
void
type argument in any other way:What can happen if I use an f() declaration?
Maybe the code will compile just fine: 6.7.5.3 Function declarators (including prototypes):
So you can get away with:
Other times, UB can creep up (and if you are lucky the compiler will tell you), and you will have a hard time figuring out why:
See: Why does an empty declaration work for definitions with int arguments but not for float arguments?
f() and f(void) for definitions
vs
are similar, but not identical.
6.7.5.3 Function declarators (including prototypes) says:
which looks similar to the description of
f(void)
.But still... it seems that:
is conforming undefined behavior, while:
is non conforming as discussed at: Why does gcc allow arguments to be passed to a function defined to be with no arguments?
TODO understand exactly why. Has to do with being a prototype or not. Define prototype.
在 C++ 中,
main()
和main(void)
之间没有区别。但是在 C 中,
main()
将使用任意数量的参数进行调用。示例:
main(void)
将在不带任何参数的情况下被调用。 如果我们尝试传递它,那么最终会导致编译器错误。例子:
In C++, there is no difference in
main()
andmain(void)
.But in C,
main()
will be called with any number of parameters.Example:
main(void)
will be called without any parameters. If we try to pass it then this ends up leading to a compiler error.Example:
除了语法差异之外,出于实用原因,许多人还更喜欢使用
void function(void)
:如果您使用搜索功能并且想要查找该函数的实现,您可以搜索
function(void)
,它将返回原型和实现。如果省略
void
,则必须搜索function()
,因此也会找到所有函数调用,从而更难以找到实际的实现。Besides syntactical differences, many people also prefer using
void function(void)
for pracitical reasons:If you're using the search function and want to find the implementation of the function, you can search for
function(void)
, and it will return the prototype as well as the implementation.If you omit the
void
, you have to search forfunction()
and will therefore also find all function calls, making it more difficult to find the actual implementation.