简体   繁体   English

c99 中的 func() 与 func(void)

[英]func() vs func(void) in c99

void func() In practice, an empty parameter means any argument is accepted. void func()实际上,空参数意味着接受任何参数。

void func(void) accepts no argument. void func(void)接受任何参数。

But in Standard C99, I find such lines:但是在标准 C99 中,我发现了这样的几行:

6.7.5.3 Function declarators (including prototypes) 6.7.5.3 函数声明符(包括原型)
14 An identifier list declares only the identifiers of the parameters of the function. 14 标识符列表仅声明函数参数的标识符。 An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters.作为该函数定义的一部分的函数声明符中的空列表指定该函数没有参数。 The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.不属于该函数定义的函数声明符中的空列表指定不提供有关参数数量或类型的信息。

according to the standard, func() and func(void) is the same?根据标准, func()func(void)是一样的吗?

TL;DR TL; 博士

In declarations,在声明中,

void func1();     // obsolescent
void func2(void);

the behaviour is quite different.行为完全不同。 The first one declares a function without any prototype - and it may take any number of arguments!第一个声明了一个没有任何原型的函数——它可以接受任意数量的参数! Whereas the latter declares a function with a prototype, that has no parameters and accepts no arguments.而后者声明了一个带有原型的函数,它没有参数并且不接受任何参数。

In definitions定义中

void func1() { }     // obsolescent

and

void func2(void) { }
  • The former declares and defines a function func1 that has no parameters and no prototype前者声明并定义了一个没有参数也没有原型的函数func1

  • The latter declares and defines a function func2 with a prototype that has no parameters.后者声明并定义了一个函数func2 ,其原型没有参数。

These two behave distinctly in that whereas the C compiler must print a diagnostic message when calling a prototyped function with wrong number of arguments, it needn't do so when calling a function without prototype.这两者的行为截然不同,因为 C 编译器在调用具有错误数量的参数的原型函数时必须打印诊断消息,而在调用没有原型的函数时则不需要这样做。

Ie, given the definitions above即,鉴于上述定义

func1(1, 2, 3); // need not produce a diagnostic message
func2(1, 2, 3); // must always produce a diagnostic message 
                // as it is a constraint violation

However both calls are illegal in strictly-conforming programs as they're explicitly undefined behaviour as per 6.5.2.2p6 .然而,这两个调用在严格遵守的程序中都是非法的,因为它们是6.5.2.2p6明确未定义的行为。

Furthermore, the empty parentheses are considered an obsolescent feature:此外,空括号被认为是一个过时的特征:

The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.使用带空括号的函数声明符(不是原型格式参数类型声明符)是一个过时的特性。

and

The use of function definitions with separate parameter identifier and declaration lists (not prototype-format parameter type and identifier declarators) is an obsolescent feature.使用具有单独参数标识符和声明列表(不是原型格式参数类型和标识符声明符)的函数定义是一个过时的功能。

In detail详细

There are 2 related, yet distinct concepts: parameters and arguments.有两个相关但不同的概念:参数和参数。

  • arguments are the values passed into the function.参数是传递给函数的值。

  • parameters are the names/variables within the function that are set to the values of the arguments when the function entered参数是函数内的名称/变量,在函数输入时设置为参数的值

In the following excerpt:在以下摘录中:

int foo(int n, char c) {
    ...
}

...

    foo(42, ch);

n and c are parameters. nc是参数。 42 and ch are arguments. 42ch是参数。

The quoted excerpt only concerns the parameters of a function, but doesn't mention anything about the prototype or arguments to the function.引用的摘录只涉及函数的参数,但没有提及函数的原型或参数。


The declaration void func1() means that the function func1 can be called with any number of arguments , ie no information about the number of arguments is specified (as a separate declaration, C99 specifies this as "function with no parameter specification), whereas the declaration void func2(void) means that the function func2 does not accept any arguments at all.声明void func1()意味着可以使用任意数量的参数调用函数func1 ,即没有指定关于参数数量的信息(作为单独的声明,C99 将此指定为“没有参数指定的函数),而声明void func2(void)意味着函数func2根本不接受任何参数

The quote in your question means that within a function definition , void func1() and void func2(void) both signal them that there are no parameters , ie variable names that are set to the values of the arguments when the function is entered.您问题中的引号意味着在函数定义中void func1()void func2(void)都向它们发出信号,表示没有参数,即在输入函数时设置为参数值的变量名称 The void func() {} contrasts with void func(); void func() {}void func();形成对比void func(); the former declares that func indeed takes no parameters, whereas the latter is a declaration for a function func for which neither parameters nor their types are specified (a declaration without prototype).前者声明func确实不带参数,而后者是对没有指定参数也没有指定类型的函数func的声明(没有原型的声明)。

However, they yet differ definition-wise in that然而,它们在定义上有所不同,因为

  • The definition void func1() {} doesn't declare a prototype, whereas void func2(void) {} does, because () is not a parameter type list, whereas (void) is a parameter type list ( 6.7.5.3.10 ):定义void func1() {}不声明原型,而void func2(void) {} ,因为()不是参数类型列表,而(void)是参数类型列表 ( 6.7.5.3.10 ):

    The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters. void 类型的未命名参数作为列表中的唯一项的特殊情况指定该函数没有参数。

    and further 6.9.1.7以及6.9.1.7

    If the declarator includes a parameter type list, the list also specifies the types of all the parameters;如果声明符包含参数类型列表,该列表还指定了所有参数的类型; such a declarator also serves as a function prototype for later calls to the same function in the same translation unit.这样的声明符也可以作为一个函数原型,供以后在同一个翻译单元中调用同一个函数。 If the declarator includes an identifier list, the types of the parameters shall be declared in a following declaration list.如果声明符包含标识符列表,则参数的类型应在以下声明列表中声明。 In either case, the type of each parameter is adjusted as described in 6.7.5.3 for a parameter type list;在任何一种情况下,每个参数的类型都按照 6.7.5.3 中针对参数类型列表的描述进行调整; the resulting type shall be an object type.结果类型应为对象类型。

    The declarator of function definition for func1 does not contain a parameter type list , and thus the function then doesn't have a prototype.函数定义为声明符func1包含参数类型列表,因而功能,那么没有一个原型。

  • void func1() { ... } can still be called with any number of arguments, whereas it is a compile-time error to call void func2(void) { ... } with any arguments (6.5.2.2): void func1() { ... }仍然可以使用任意数量的参数调用,而使用任何参数调用void func2(void) { ... }是编译时错误(6.5.2.2):

    If the expression that denotes the called function has a type that includes a prototype , the number of arguments shall agree with the number of parameters.如果表示被调用函数的表达式的类型包含原型,则参数数量应与参数数量一致。 Each argument shall have a type such that its value may be assigned to an object with the unqualified version of the type of its corresponding parameter.每个参数都应该有一个类型,这样它的值可以分配给一个对象,该对象具有其相应参数的类型的非限定版本。

    (emphasis mine) (强调我的)

    This is a constraint , which according of the standard says that a conforming implementation must display at least one diagnostic message about this problem.这是一个约束,根据标准,符合标准的实现必须至少显示一条有关此问题的诊断消息。 But since func1 doesn't have a prototype, a conforming implementation is not required to produce any diagnostics.但是由于func1没有原型,因此不需要符合标准的实现来生成任何诊断信息。


However, if the number of arguments does not equal the number of parameters, the behaviour is undefined 6.5.2.2p6 :但是,如果参数数量不等于参数数量,则行为未定义6.5.2.2p6

If the expression that denotes the called function has a type that does not include a prototype , [...] If the number of arguments does not equal the number of parameters, the behavior is undefined.如果表示被调用函数的表达式的类型不包含原型,[...]如果参数数量不等于参数数量,则行为未定义。

So in theory a conforming C99 compiler is also allowed to error or diagnose a warning in this case.因此,理论上,在这种情况下,也允许符合 C99 的编译器出错或诊断警告。 StoryTeller provided evidence that clang might diagnose this ; StoryTeller提供的证据表明clang 可能会对此做出诊断 however, my GCC doesn't seem to do it (and this might also be required for it to be compatible with some old obscure code too):但是,我的 GCC 似乎没有这样做(这也可能需要它与一些旧的晦涩代码兼容):

void test() { }

void test2(void) { }

int main(void) {
    test(1, 2);
    test2(1, 2);
}

When the above program is compiled with gcc -std=c99 test.c -Wall -Werror , the output is:当上面的程序用gcc -std=c99 test.c -Wall -Werror ,输出为:

test.c: In function ‘main’:
test.c:7:5: error: too many arguments to function ‘test2’
     test2(1, 2);
     ^~~~~
test.c:3:6: note: declared here
 void test2(void) { }
      ^~~~~

That is, the arguments are not checked at all against the parameters of a function whose declaration in definition is not prototyped ( test ) whereas GCC considers it as a compile-time error to specify any arguments to a prototyped function ( test2 );也就是说,对于定义中的声明不是原型( test )的函数的参数,根本不检查参数,而 GCC 认为将任何参数指定给原型函数( test2 )是编译时错误; any conforming implementation must diagnose this as it is a constraint violation.任何符合要求的实现都必须对此进行诊断,因为它违反了约束。

The significant part of the quote is highlighted in bold below:引文的重要部分在下面以粗体突出显示:

6.7.5.3 Function declarators (including prototypes) 14 An identifier list declares only the identifiers of the parameters of the function. 6.7.5.3 函数声明符(包括原型) 14 标识符列表仅声明函数参数的标识符。 An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters.作为该函数定义一部分的函数声明符中的空列表指定该函数没有参数。 The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.不属于该函数定义的函数声明符中的空列表指定不提供有关参数数量或类型的信息。

So, when the parameter list is empty for a function with its body, they are the same.因此,当函数体的参数列表为空时,它们是相同的。 But of it is just a declaration of a function.但它只是一个函数的声明。

void function1(); // No information about arguments
void function2(void); // Function with zero arguments

void function3() {
    // Zero arguments
}

void function4(void) {
    // Zero arguments
}

according to the standard, func() and func(void) is the same?根据标准,func() 和 func(void) 是一样的吗?

No. func(void) says the function takes no arguments at all;不是。 func(void)表示该函数根本不接受任何参数; whereas func() says the function takes an unspecified number of arguments.func()表示该函数采用未指定数量的参数。 Both are valid but the func() style are obsolete and shouldn't be used.两者都是有效的,但func()样式已过时,不应使用。

This is an artifact from pre-standard C. C99 marked this as obsolete.这是来自前标准 C 的工件。C99 将其标记为过时。

6.11.6 Function declarators : 6.11.6 函数声明符

The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.使用带空括号的函数声明符(不是原型格式参数类型声明符)是一个过时的特性。

As of C11, it still remains as obsolescent and hasn't been removed from the standard.从 C11 开始,它仍然过时并且没有从标准中删除。

The empty parameter list inside a function definition means that it does not include a prototype nor has any parameters.函数定义中的空参数列表意味着它不包含原型,也没有任何参数。

C11 §6.9.1/7 Function definitions (emphasis in ongoing quotes is mine) C11 §6.9.1/7函数定义(持续引用中的重点是我的)

The declarator in a function definition specifies the name of the function being defined and the identifiers of its parameters.函数定义中的声明符指定被定义函数的名称及其参数的标识符。 If the declarator includes a parameter type list , the list also specifies the types of all the parameters;如果声明包含参数类型 list ,则该列表还指定了所有参数的类型; such a declarator also serves as a function prototype for later calls to the same function in the same translation unit.这样的声明符也可以作为一个函数原型,供以后在同一个翻译单元中调用同一个函数。

The question asks:问题问:

according to the standard, func() and func(void) is the same?根据标准, func()func(void)是一样的吗?

No. The essential difference between void func() and void func(void) lies in their calls.不是。 void func()void func(void)之间的本质区别在于它们的调用。

C11 §6.5.2.2/2 Function calls (within constraints section): C11 §6.5.2.2/2函数调用(在约束部分内):

If the expression that denotes the called function has a type that includes a prototype , the number of arguments shall agree with the number of parameters .如果表示被调用函数的表达式的类型包括原型,则参数数量应与参数数量一致 Each argument shall have a type such that its value may be assigned to an object with the unqualified version of the type of its corresponding parameter.每个参数都应该有一个类型,这样它的值可以分配给一个对象,该对象具有其相应参数的类型的非限定版本。

Notice that parameters ≠ arguments.请注意,参数≠ 参数。 The function may contain no parameters, but it may have multiple arguments.该函数可能不包含任何参数,但它可能有多个参数。

As function defined with empty parameters does not introduce a prototype, it's not checked against its calls, so in theory it may be supplied with whatever number of arguments.由于使用空参数定义的函数不会引入原型,因此不会检查其调用,因此理论上可以为它提供任何数量的参数。

However, it is technically an undefined behavior to call such function with at least one argument (see Antti Haapala's comments ).但是,从技术上讲,使用至少一个参数调用此类函数是一种未定义的行为(请参阅 Antti Haapala 的评论)。

C11 §6.5.2.2/6 Function calls (within semantics section): C11 §6.5.2.2/6函数调用(在语义部分):

If the number of arguments does not equal the number of parameters, the behavior is undefined.如果参数数量不等于参数数量,则行为未定义。

Hence, the difference is subtle:因此,差异是微妙的:

  • When a function is defined with void , it won't compile when number of arguments don't match with parameters (along with their types), because of constaint violation (§6.5.2.2/2).当一个函数用void定义时,当参数数量与参数(以及它们的类型)不匹配时,它不会编译,因为违反了约束(第 6.5.2.2/2 节)。 Such situation requires diagnostic message from conforming compiler.这种情况需要来自符合标准的编译器的诊断消息。
  • If it is defined with empty parameters, it may or may not compile (there is no requirement for diagnostic message from conforming compiler), however it's UB to call such function.如果它是用空参数定义的,它可能会可能不会编译(没有要求来自符合编译器的诊断消息),但是调用这样的函数是 UB。

Example:例子:

#include <stdio.h>

void func1(void) { puts("foo"); }
void func2()     { puts("foo"); }

int main(void)
{
    func1(1, 2); // constraint violation, it shouldn't compile
    func2(3, 4); // may or may not compile, UB when called
    return 0;
}

Note that optimizing compiler may cut off the arguments in such case.请注意,在这种情况下,优化编译器可能会切断参数。 For instance, this is how Clang compiles the above code (excluding func1 's call) with -01 on x86-64 according to the SysV ABI calling conventions:例如,这是 Clang 根据 SysV ABI 调用约定在 x86-64 上使用-01编译上述代码(不包括func1的调用)的方式:

main:                                   # @main
        push    rax          ; align stack to the 16-byte boundary
        call    func2        ; call func2 (no arguments given)
        xor     eax, eax     ; set zero as return value
        pop     rcx          ; restore previous stack position (RSP)
        ret

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM