I don't get why does this code compile?
我不明白为什么这段代码要编译?
#include <stdio.h>
void foo() {
printf("Hello\n");
}
int main() {
const char *str = "bar";
foo(str);
return 0;
}
gcc doesn't even throw a warning that I am passing too many arguments to foo(). Is this expected behavior?
gcc甚至没有向foo()传递太多参数的警告。这是预期行为吗?
4 个解决方案
#1
82
In C, a function declared with an empty parameter list accepts an arbitrary number of arguments when being called, which are subject to the usual arithmetic promotions. It is the responsibility of the caller to ensure that the arguments supplied are appropriate for the definition of the function.
在C语言中,使用空参数列表声明的函数在被调用时接受任意数量的参数,这受到通常的算术提升。调用方的职责是确保提供的参数适合于函数的定义。
To declare a function taking zero arguments, you need to write void foo(void);
.
若要声明函数为零参数,则需要写入void foo(void);
This is for historic reasons; originally, C functions didn't have prototypes, as C evolved from B, a typeless language. When prototypes were added, the original typeless declarations were left in the language for backwards compatibility.
这是由于历史原因;最初,C函数没有原型,因为C是从一种没有类型的语言B演化而来的。当添加原型时,为了向后兼容,语言中保留了原始的无类型声明。
To get gcc to warn about empty parameter lists, use -Wstrict-prototypes
:
要让gcc对空参数列表发出警告,请使用- wstrict原型:
Warn if a function is declared or defined without specifying the argument types. (An old-style function definition is permitted without a warning if preceded by a declaration which specifies the argument types.)
警告如果函数被声明或定义,而不指定参数类型。(如果前面的声明指定了参数类型,则不需要警告就可以使用旧式函数定义。)
#2
52
For legacy reasons, declaring a function with ()
for a parameter list essentially means “figure out the parameters when the function is called”. To specify that a function has no parameters, use (void)
.
由于遗留原因,为参数列表声明带有()的函数本质上意味着“在调用函数时找出参数”。要指定一个函数没有参数,请使用(void)。
Edit: I feel like I am racking up reputation in this problem for being old. Just so you kids know what programming used to be like, here is my first program. (Not C; it shows you what we had to work with before that.)
编辑:我觉得我在这个问题上的名声越来越坏了。为了让你们这些孩子知道以前编程是什么样子,这是我的第一个程序。(不是C;它向你展示了我们之前的工作。
#3
10
void foo() {
printf("Hello\n");
}
foo(str);
in C, this code does not violates a constraint (it would if it was defined in its prototype-form with void foo(void) {/*...*/}
) and as there is no constraint violation, the compiler is not required to issue a diagnostic.
在C中,这段代码没有违反约束(如果它是在它的原型形式中定义为void foo(void){/*. */}),并且由于没有约束冲突,编译器就不需要进行诊断。
But this program has undefined behavior according to the following C rules:
但本程序有以下C规则未定义的行为:
From:
来自:
(C99, 6.9.1p7) "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,142) the types of the parameters shall be declared in a following declaration list."
(C99 6.9.1p7)“如果声明者包含参数类型列表,则列表还指定所有参数的类型;这样的声明符还作为一个函数原型,用于以后在相同的翻译单元中调用相同的函数。如果声明者包含一个标识符列表,142)参数的类型应在以下声明列表中声明。
the foo
function does not provide a prototype.
foo函数没有提供原型。
From:
来自:
(C99, 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."
(C99 6.5.2.2p6)如果表示被调用函数的表达式有一个不包含原型的类型[…如果参数的数量不等于参数的数量,那么行为就没有定义。
the foo(str)
function call is undefined behavior.
foo(str)函数调用是未定义的行为。
C does not mandate the implementation to issue a diagnostic for a program that invokes undefined behavior but your program is still an erroneous program.
C并不要求实现为调用未定义行为的程序发出诊断,但是您的程序仍然是一个错误的程序。
#4
5
Both the C99 Standard (6.7.5.3) and the C11 Standard (6.7.6.3) state:
C99标准(6.7.5.3)和C11标准(6.7.6.3)均声明:
An identifier list declares only the identifiers of the parameters of the function. 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.
标识符列表只声明函数参数的标识符。函数声明器中的空列表是函数定义的一部分,它指定函数没有参数。函数声明符中的空列表不是该函数定义的一部分,它指定不提供关于参数的数量或类型的信息。
Since the declaration of foo is part of a definition, the declaration specifies that foo takes 0 arguments, so the call foo(str) is at least morally wrong. But as described below, there are different degrees of "wrong" in C, and compilers may differ in how they deal with certain kinds of "wrong".
因为foo的声明是定义的一部分,声明指定foo接受0个参数,所以foo(str)至少在道德上是错误的。但是如下所述,C中有不同程度的“错误”,编译器在处理特定类型的“错误”时可能存在不同的方式。
To take a slightly simpler example, consider the following program:
举个简单一点的例子,考虑下面的程序:
int f() { return 9; }
int main() {
return f(1);
}
If I compile the above using Clang:
如果我使用Clang来编译上面的代码:
tmp$ cc tmp3.c
tmp3.c:4:13: warning: too many arguments in call to 'f'
return f(1);
~ ^
1 warning generated.
If I compile with gcc 4.8 I don't get any errors or warnings, even with -Wall. A previous answer suggested using -Wstrict-prototypes, which correctly reports that the definition of f is not in prototype form, but this is really not the point. The C Standard(s) allow a function definition in a non-prototype form such as the one above and the Standards clearly state that this definition specifies that the function takes 0 arguments.
如果我使用gcc 4.8编译,就不会出现任何错误或警告,即使是使用-Wall。之前的回答建议使用- wstrict原型,它正确地报告了f的定义不是原型形式,但这并不是重点。C标准允许非原型形式的函数定义,如上面的,标准明确规定这个定义指定函数接受0个参数。
Now there is a constraint (C11 Sec. 6.5.2.2):
现在有一个约束(C11 Sec 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.
如果表示所调用函数的表达式具有包含原型的类型,则参数的数量应与参数的数量一致。
However, this constraint does not apply in this case, since the type of the function does not include a prototype. But here is a subsequent statement in the semantics section (not a "constraint"), that does apply:
但是,这个约束在这种情况下不适用,因为函数的类型不包含原型。但在语义部分(不是“约束”)有一个后续语句,它确实适用:
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.
如果表示被调用函数的表达式具有不包含原型的类型……如果参数的数量不等于参数的数量,则行为是未定义的。
Hence the function call does result in undefined behavior (i.e., the program is not "strictly conforming"). However, the Standard only requires an implementation to report a diagnostic message when an actual constraint is violated, and in this case, there is no violation of a constraint. Hence gcc is not required to report an error or warning in order to be a "conforming implementation".
因此函数调用确实会导致未定义的行为(例如。,该程序并非“严格遵守”)。但是,该标准只要求实现在实际的约束被违反时报告诊断消息,在这种情况下,没有违反约束。因此,gcc不需要报告错误或警告以作为“符合标准的实现”。
So I think the answer to the question, why does gcc allow it?, is that gcc is not required to report anything, since this is not a constraint violation. Moreover gcc does not claim to report every kind of undefined behavior, even with -Wall or -Wpedantic. It is undefined behavior, which means the implementation can choose how to deal with it, and gcc has chosen to compile it without warnings (and apparently it just ignores the argument).
所以我认为问题的答案是,为什么gcc允许它?,是因为gcc不需要报告任何东西,因为这不是一个约束冲突。此外,gcc并不宣称报告所有未定义的行为,即使使用-Wall或-Wpedantic。它是未定义的行为,这意味着实现可以选择如何处理它,gcc选择在没有警告的情况下编译它(显然它只是忽略了参数)。
#1
82
In C, a function declared with an empty parameter list accepts an arbitrary number of arguments when being called, which are subject to the usual arithmetic promotions. It is the responsibility of the caller to ensure that the arguments supplied are appropriate for the definition of the function.
在C语言中,使用空参数列表声明的函数在被调用时接受任意数量的参数,这受到通常的算术提升。调用方的职责是确保提供的参数适合于函数的定义。
To declare a function taking zero arguments, you need to write void foo(void);
.
若要声明函数为零参数,则需要写入void foo(void);
This is for historic reasons; originally, C functions didn't have prototypes, as C evolved from B, a typeless language. When prototypes were added, the original typeless declarations were left in the language for backwards compatibility.
这是由于历史原因;最初,C函数没有原型,因为C是从一种没有类型的语言B演化而来的。当添加原型时,为了向后兼容,语言中保留了原始的无类型声明。
To get gcc to warn about empty parameter lists, use -Wstrict-prototypes
:
要让gcc对空参数列表发出警告,请使用- wstrict原型:
Warn if a function is declared or defined without specifying the argument types. (An old-style function definition is permitted without a warning if preceded by a declaration which specifies the argument types.)
警告如果函数被声明或定义,而不指定参数类型。(如果前面的声明指定了参数类型,则不需要警告就可以使用旧式函数定义。)
#2
52
For legacy reasons, declaring a function with ()
for a parameter list essentially means “figure out the parameters when the function is called”. To specify that a function has no parameters, use (void)
.
由于遗留原因,为参数列表声明带有()的函数本质上意味着“在调用函数时找出参数”。要指定一个函数没有参数,请使用(void)。
Edit: I feel like I am racking up reputation in this problem for being old. Just so you kids know what programming used to be like, here is my first program. (Not C; it shows you what we had to work with before that.)
编辑:我觉得我在这个问题上的名声越来越坏了。为了让你们这些孩子知道以前编程是什么样子,这是我的第一个程序。(不是C;它向你展示了我们之前的工作。
#3
10
void foo() {
printf("Hello\n");
}
foo(str);
in C, this code does not violates a constraint (it would if it was defined in its prototype-form with void foo(void) {/*...*/}
) and as there is no constraint violation, the compiler is not required to issue a diagnostic.
在C中,这段代码没有违反约束(如果它是在它的原型形式中定义为void foo(void){/*. */}),并且由于没有约束冲突,编译器就不需要进行诊断。
But this program has undefined behavior according to the following C rules:
但本程序有以下C规则未定义的行为:
From:
来自:
(C99, 6.9.1p7) "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,142) the types of the parameters shall be declared in a following declaration list."
(C99 6.9.1p7)“如果声明者包含参数类型列表,则列表还指定所有参数的类型;这样的声明符还作为一个函数原型,用于以后在相同的翻译单元中调用相同的函数。如果声明者包含一个标识符列表,142)参数的类型应在以下声明列表中声明。
the foo
function does not provide a prototype.
foo函数没有提供原型。
From:
来自:
(C99, 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."
(C99 6.5.2.2p6)如果表示被调用函数的表达式有一个不包含原型的类型[…如果参数的数量不等于参数的数量,那么行为就没有定义。
the foo(str)
function call is undefined behavior.
foo(str)函数调用是未定义的行为。
C does not mandate the implementation to issue a diagnostic for a program that invokes undefined behavior but your program is still an erroneous program.
C并不要求实现为调用未定义行为的程序发出诊断,但是您的程序仍然是一个错误的程序。
#4
5
Both the C99 Standard (6.7.5.3) and the C11 Standard (6.7.6.3) state:
C99标准(6.7.5.3)和C11标准(6.7.6.3)均声明:
An identifier list declares only the identifiers of the parameters of the function. 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.
标识符列表只声明函数参数的标识符。函数声明器中的空列表是函数定义的一部分,它指定函数没有参数。函数声明符中的空列表不是该函数定义的一部分,它指定不提供关于参数的数量或类型的信息。
Since the declaration of foo is part of a definition, the declaration specifies that foo takes 0 arguments, so the call foo(str) is at least morally wrong. But as described below, there are different degrees of "wrong" in C, and compilers may differ in how they deal with certain kinds of "wrong".
因为foo的声明是定义的一部分,声明指定foo接受0个参数,所以foo(str)至少在道德上是错误的。但是如下所述,C中有不同程度的“错误”,编译器在处理特定类型的“错误”时可能存在不同的方式。
To take a slightly simpler example, consider the following program:
举个简单一点的例子,考虑下面的程序:
int f() { return 9; }
int main() {
return f(1);
}
If I compile the above using Clang:
如果我使用Clang来编译上面的代码:
tmp$ cc tmp3.c
tmp3.c:4:13: warning: too many arguments in call to 'f'
return f(1);
~ ^
1 warning generated.
If I compile with gcc 4.8 I don't get any errors or warnings, even with -Wall. A previous answer suggested using -Wstrict-prototypes, which correctly reports that the definition of f is not in prototype form, but this is really not the point. The C Standard(s) allow a function definition in a non-prototype form such as the one above and the Standards clearly state that this definition specifies that the function takes 0 arguments.
如果我使用gcc 4.8编译,就不会出现任何错误或警告,即使是使用-Wall。之前的回答建议使用- wstrict原型,它正确地报告了f的定义不是原型形式,但这并不是重点。C标准允许非原型形式的函数定义,如上面的,标准明确规定这个定义指定函数接受0个参数。
Now there is a constraint (C11 Sec. 6.5.2.2):
现在有一个约束(C11 Sec 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.
如果表示所调用函数的表达式具有包含原型的类型,则参数的数量应与参数的数量一致。
However, this constraint does not apply in this case, since the type of the function does not include a prototype. But here is a subsequent statement in the semantics section (not a "constraint"), that does apply:
但是,这个约束在这种情况下不适用,因为函数的类型不包含原型。但在语义部分(不是“约束”)有一个后续语句,它确实适用:
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.
如果表示被调用函数的表达式具有不包含原型的类型……如果参数的数量不等于参数的数量,则行为是未定义的。
Hence the function call does result in undefined behavior (i.e., the program is not "strictly conforming"). However, the Standard only requires an implementation to report a diagnostic message when an actual constraint is violated, and in this case, there is no violation of a constraint. Hence gcc is not required to report an error or warning in order to be a "conforming implementation".
因此函数调用确实会导致未定义的行为(例如。,该程序并非“严格遵守”)。但是,该标准只要求实现在实际的约束被违反时报告诊断消息,在这种情况下,没有违反约束。因此,gcc不需要报告错误或警告以作为“符合标准的实现”。
So I think the answer to the question, why does gcc allow it?, is that gcc is not required to report anything, since this is not a constraint violation. Moreover gcc does not claim to report every kind of undefined behavior, even with -Wall or -Wpedantic. It is undefined behavior, which means the implementation can choose how to deal with it, and gcc has chosen to compile it without warnings (and apparently it just ignores the argument).
所以我认为问题的答案是,为什么gcc允许它?,是因为gcc不需要报告任何东西,因为这不是一个约束冲突。此外,gcc并不宣称报告所有未定义的行为,即使使用-Wall或-Wpedantic。它是未定义的行为,这意味着实现可以选择如何处理它,gcc选择在没有警告的情况下编译它(显然它只是忽略了参数)。