是的,它們是不同的;第二個是正確,第一個作爲整個是錯誤的。它是那麼錯誤,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