是的,它们是不同的;第二个是正确,第一个作为整个是错误的。它是那么错误,GCC 5.2.1拒绝完全编译它。它适用于你的一切都只是一个fluke:
/* this coupled with */
int function1();
int main() {
/* this */
function1(x, y);
}
/* and this one leads to undefined behaviour */
int function1(int x, float y) {
/* ... */
}
在上面的代码中,声明int function1();
没有指定的参数类型(它没有原型),这被认为是一个过时功能在C11(和C89,C99就此而言)标准。如果调用这种函数,则默认参数促销将在参数上完成:int
按原样传递,但float
被提升为double
。
由于您的实际函数需要参数(int, float)
而不是(int, double)
,这会导致未定义的行为。即使你的函数预期为(int, double)
但是y
是一个整数,或者说你用function1(0, 0);
而不是function(0, 0.0);
来调用它,你的程序仍然会有未定义的行为。幸运的GCC 5.2.1声明说的function1
的声明和定义是矛盾的:
% gcc test.c
test.c:9:5: error: conflicting types for ‘function1’
int function1(int x, float y) {
^
test.c:9:1: note: an argument type that has a default promotion can’t
match an empty parameter name list declaration
int function1(int x, float y) {
^
test.c:1:5: note: previous declaration of ‘function1’ was here
int function1();
^
test.c:12:5: error: conflicting types for ‘function2’
int function2(int x, float y) {
^
test.c:12:1: note: an argument type that has a default promotion can’t
match an empty parameter name list declaration
int function2(int x, float y) {
^
test.c:2:5: note: previous declaration of ‘function2’ was here
int function2();
^
和编译器退出,错误代码,而我tcc
编译它令人高兴的是,没有任何诊断,没有什么。它只是产生破损的代码。当然,如果你在头文件中有声明,并且在不包含该声明的不同编译单元中定义,情况也是如此。
现在,如果编译器检测不到这种情况下,在运行时,什么可能发生,如预期的未定义行为。
例如,假设参数在栈上传递的情况;在32位处理器上int
和float
可能适合4个字节,而double
可能是8个字节;然后函数调用将推动x
为int
和y
为double
即使是float
- 总来电会推12个字节和被叫只能期待8
在另一种情况下,假设你会打电话来的函数与2个整数。调用代码然后将这些加载到整数寄存器中,但调用者期望在浮点寄存器中加倍。浮点寄存器可以包含一个陷阱值,当它被访问时会终止你的程序。
什么是最坏的情况,你的程序可能现在表现预期,从而包含heisenbug当你重新编译编译器的端口是一个较新的版本,或到另一个平台的代码,可能会出现问题。
您的前两行用不同于以后声明的签名(不带参数)的函数声明函数。所以前两行是不需要的。函数声明描述参数的名称,数量和类型以及返回类型。两个函数可以具有相同的名称但参数不同。它们不能仅在返回类型上有所不同。 – BryanT
@BryanT这是不正确的(尽管这在C++中是正确的)。在C语言中,函数声明中的空括号表示可以使用_any_类型的_any_个参数。如果你明确想要零参数,使用'(void)';参见'main'的标准签名之一:'int main(void){...}'。 – szczurcio
我同意。我在想C++。但是,将它们完全按照它们的定义进行宣布并不是更好吗? – BryanT