2014-07-11 84 views
4

我剛剛注意到GCC的行爲,這對我來說似乎很陌生(未與其他編譯器一起檢查過)。調用帶有太多參數的函數時的GCC行爲

如果我編譯此代碼:

#include <stdio.h> 

void foo(int i) 
{ 
    printf("Hello %d\n",i); 
} 

int main(){ 
    foo(1, 2); 
    return 0; 
} 

我會得到一個編譯錯誤:

test.c:9:5: error: too many arguments to function ‘foo’ 

但如果我編譯此代碼:

#include <stdio.h> 

void foo() 
{ 
    printf("Hello\n"); 
} 

int main(){ 
    foo(1, 2); 
    return 0; 
} 

我沒有得到任何錯誤或警告。

有人能解釋我爲什麼嗎?

我用gcc 4.6.3試用了一下手臂,無 - EABI-GCC 4.8.3

編輯:我編譯所有的警告:gcc -Wall test.c

+1

加上'-pedantic'獲得更多的警告 – mch

+1

@mch謝謝你的提示,在這種情況下,它不會警告任何 – Quentin

+0

您需要-Wstrict的原型讓海灣合作委員會警告它。 –

回答

6

在C中,編寫void foo()意味着foo帶有未指定數量的參數。

爲了指示功能foo()應該採取任何參數,你應該寫void foo(void)

出於這個原因,你也應該使用的簽名int main(void)

+0

不錯!我不知道爲參數指定'void'是不同於將其保留爲空 – Quentin

+1

@Quentin它僅在C中才是如此。在C++中,它們是同義詞。 –

+1

@PascalCuoq'(void)'然而在C++中被棄用,因爲它只是所述C規則的向後兼容的補餘。另外,'main(void)',真的嗎? – Quentin

2

打開你的警告!

void foo() 

是一種陳舊的ANSI C語言,它沒有適當的原型來聲明函數。如果你這樣做,該函數的行爲類似於void foo(...),並允許任意數量的參數傳遞。

(在C++中,void foo()聲明瞭一個null-arity函數,就像你期望的那樣)。

+1

我沒有警告gcc -Wall test.c' – Quentin

+1

我想警告是編譯器依賴的。我確實收到了Clang的警告。 – nneonneo

2

我會爭辯說,gcc應該在這裏抱怨。艾蒂安的答案應該是正確的,如果f是一個extern原型,但(在C996.7.6.3§14在C11,6.7.5.3§14)實際段落討論這一點讀取方式(重點煤礦):

標識符列表僅聲明該函數參數的標識符。 作爲函數定義的一部分的函數聲明器中的空 列表指定 函數沒有參數。函數聲明器中的空列表不是該函數的 定義的一部分,它指定不提供有關參數數量或類型的信息。

clang(V3.4)確實發出警告(too many arguments in call to 'foo')與你的文件,但會愉快地(默默)編譯下面的兩個文件:

foo.c的:

extern void foo(); 
int main(){ 
    foo(1, 2); 
    return 0; 
} 

吧。C:

#include <stdio.h> 
void foo (int x, int y, int z) { printf("Hello %d\n", z); } 

結果:

$ clang -o foo bar.c foo.c 
$ ./foo 
Hello 1405669720 
+0

+1。你知道爲什麼gcc的行爲如此嗎?我搜索了gcc的文檔,但沒有找到任何參考。 –

+0

我不會不同意它*應該*警告,但我不相信這是必需的。具有空括號的函數定義不提供原型。它沒有指定任何參數(在函數定義中可見的局部對象),但是(從奇怪的意義上)它沒有指定該函數不需要*參數*。該語法是預ANSI C的遺留物;當時,編譯器通常不會警告參數不匹配。教訓:始終使用原型。 –

+0

使用gcc,你可以定義函數屬性'__attribute__格式(類型,字符串,檢查)',強制編譯器根據格式字符串檢查'printf()'或'scanf()'函數參數。 這可能是您在標準庫標題中找到的內容。 – mfro