向参数为 0 的函数发送参数时的行为不一致
Inconsistent behavior when sending parameters to a function with 0 parameters
以下代码在 clang (3.6.0) 和 gcc(4.9.2) 上产生编译错误 (coliru link)
#include <stdio.h>
void foo(void){
printf("lalala\n");
}
int main(void)
{
foo(1, 2, 3, 4, 5, 6, 67);
return 0;
}
而 VS2013(使用 /TC /W4 编译时)只产生警告
warning C4087: 'foo' : declared with 'void' parameter list ... 9 1
这是 VC 中的错误还是 clang 和 gcc 太苛刻了?
当您调用带参数的函数时,每个参数都会在您程序跳转到调用的函数之前被压入您的调用堆栈。然后,当你到达函数时,只有参数指定的数据被删除,或者实际上堆栈指针只移动到它应该为函数声明指定的参数移动的距离。在你的函数 returns 之后,你的堆栈指针将不会位于正确的位置,因为它在函数调用期间会向下移动太远。我不确定编译器如何处理这个问题,但如果您从多个函数返回,这样做似乎会导致问题。
来自 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. [...]
VC 被故意允许以允许编译(为了兼容)不正确的代码。这是标准允许的,因为在遇到某些类型的不正确代码时,只要还发出诊断,就允许发出程序。
根据C99 gcc
和clang
都是正确的,参数列表中的void
表示它不带参数。来自 c99 标准草案 6.7.5.3
函数声明符(包括原型):
The special case of an unnamed parameter of type void as the only item in the list
specifies that the function has no parameters.
但由于 Visual Studio 正在发布诊断,因此它是一个有效的扩展。这与 C4087:
的文档一致
The function declaration has no formal parameters, but the function
call has actual parameters. Extra parameters are passed according to
the calling convention of the function.
This warning is for the C compiler.
虽然文档没有升级到 Visual Stuido 2013。
该函数不带任何参数,但您正在传递参数。所以这是 约束违规 。在这种情况下,编译器需要发出 诊断。所有三个编译器都发出诊断,因此符合标准。但是它是被分类为 warning 还是 error 是编译器的选择。
所以 Visual Studio 中没有 "bug",只是 GCC 和 clang 在这种情况下更加严格。也许,VS 实现者认为它不够严重,因为传递的参数无论如何都会被忽略并决定只发出警告,我只能推测。
您的代码是否一定会失败取决于调用约定,编译器构建器可以按照他们的意愿实现。因此,一个编译器将其标记为错误而另一个编译器仅发出警告是合理的。但我几乎总是将编译器设置得尽可能严格,在 MSVS 中“警告为错误”——传递意外参数的最可能原因是你的一些错误。
以下代码在 clang (3.6.0) 和 gcc(4.9.2) 上产生编译错误 (coliru link)
#include <stdio.h>
void foo(void){
printf("lalala\n");
}
int main(void)
{
foo(1, 2, 3, 4, 5, 6, 67);
return 0;
}
而 VS2013(使用 /TC /W4 编译时)只产生警告
warning C4087: 'foo' : declared with 'void' parameter list ... 9 1
这是 VC 中的错误还是 clang 和 gcc 太苛刻了?
当您调用带参数的函数时,每个参数都会在您程序跳转到调用的函数之前被压入您的调用堆栈。然后,当你到达函数时,只有参数指定的数据被删除,或者实际上堆栈指针只移动到它应该为函数声明指定的参数移动的距离。在你的函数 returns 之后,你的堆栈指针将不会位于正确的位置,因为它在函数调用期间会向下移动太远。我不确定编译器如何处理这个问题,但如果您从多个函数返回,这样做似乎会导致问题。
来自 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. [...]
VC 被故意允许以允许编译(为了兼容)不正确的代码。这是标准允许的,因为在遇到某些类型的不正确代码时,只要还发出诊断,就允许发出程序。
根据C99 gcc
和clang
都是正确的,参数列表中的void
表示它不带参数。来自 c99 标准草案 6.7.5.3
函数声明符(包括原型):
The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.
但由于 Visual Studio 正在发布诊断,因此它是一个有效的扩展。这与 C4087:
的文档一致The function declaration has no formal parameters, but the function call has actual parameters. Extra parameters are passed according to the calling convention of the function.
This warning is for the C compiler.
虽然文档没有升级到 Visual Stuido 2013。
该函数不带任何参数,但您正在传递参数。所以这是 约束违规 。在这种情况下,编译器需要发出 诊断。所有三个编译器都发出诊断,因此符合标准。但是它是被分类为 warning 还是 error 是编译器的选择。
所以 Visual Studio 中没有 "bug",只是 GCC 和 clang 在这种情况下更加严格。也许,VS 实现者认为它不够严重,因为传递的参数无论如何都会被忽略并决定只发出警告,我只能推测。
您的代码是否一定会失败取决于调用约定,编译器构建器可以按照他们的意愿实现。因此,一个编译器将其标记为错误而另一个编译器仅发出警告是合理的。但我几乎总是将编译器设置得尽可能严格,在 MSVS 中“警告为错误”——传递意外参数的最可能原因是你的一些错误。