2014-03-31 48 views
-2

我有下面的代碼可以成功執行,但是如果函數printit沒有傳遞變量的數據類型,怎麼可能呢?然後爲什麼在輸出浮點數值打印正確,但字符值不是。我甚至試圖通過類型化來嘗試char允許在不告訴數據類型的情況下傳遞變量?

main() 
{ 
    float a = 15.5 ; 
    char ch = 'C' ; 
    printit (a, ch) ; 
} 

printit (a, ch) 
{ 
    printf ("\n%f %c", a, ch) ; 
} 

它的執行給出結果:

15.500000 ╠ 
+2

您正在使用K&R C的默認int行爲,並向編譯器說謊,因此您所期望的只是未定義的行爲。 –

+0

你使用什麼C編譯器? –

+0

g ++'作爲代碼塊中的默認設置 – user3392464

回答

3

在預標準C,這是允許的,但即便這樣也沒有鼓勵,編寫代碼的方式。沒有原型;在沒有類型說明符的情況下,變量類型爲int

如果類型不int,你可能會這樣寫:

print_it_2(a, b, c) 
    struct dohickey *c; 
    double b; 
{ 
    printf("%d %p %f\n", a, b, c); 
} 

注意,變量在一個順序列出,並在另一個定義,a仍然是一個int,因爲它不是在所有上市。函數的返回類型也被假定爲int。但是,特別是如果該函數畢竟沒有返回值(這是在支持返回類型void之前),則人們將從該聲明中省略返回類型,並且不從該函數返回值。

這不再是好風格。如果編譯器不抱怨它,它應該。我編譯額外的選項的代碼(特別是對SO答案):

gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \ 
     -Wold-style-definition -Werror ll3.c -o ll3 

丟失的樣機和嚴格的原型和老式的定義是關於清除日期這樣的老,預標準代碼的最後遺蹟。我並不總是(通常)在我的答案中顯示這三個選項,但是當我編譯代碼時,它們就在那裏。這意味着你會看到功能static(爲了避免丟失原型)和int main(void)而不是int main()(爲了避免嚴格的原型),舊樣式的定義會大聲討論問題中討論的功能。


問題的第二部分是'爲什麼浮動值打印正確,字符不是'。

其中非原型功能變化的方式之一是應用自動升級。小於int(因此charshort)的任何類型被提升爲int,並且float被提升爲double(略微簡化)。因此,在致電printit(a, ch)中,實際傳遞的值爲doubleint。在函數內部,代碼認爲aint(具體性,4字節int),以及ch。這兩個4字節數量被複制到堆棧上(再次略微簡化),並傳遞到printf()。現在,printf()是一個可變參數函數(帶有可變數量的參數),並且作爲省略號...的一部分傳遞的參數也自動提升。因此,在printf()內部,有人告訴我堆棧中有8個字節,其中包含一個double的值,當然,調用代碼傳遞了8個字節(雖然是兩個4字節單元),所以double被神奇地保存下來。然後printf()被告知棧上還有一個額外的int,但int沒有被調用代碼放在那裏,所以它讀取了一些其他不確定的值並打印該字符。

這種混亂是原型防止的。這就是爲什麼你永遠不應該再以這種風格來編碼(在這個千年乃至上一個千年的最後十年的後半期都是如此)。


Peter Schneider要求在comment

你能指出相關的標準,它允許或禁止未聲明的參數?如果確實被禁止,爲什麼gcc在被問到時不符合標準?

以代碼與GCC 4.8.2在Mac OS X 10.9.2編譯,我得到:

$ gcc -std=c11 -c xyz.c 
xyz.c:1:1: warning: return type defaults to ‘int’ [enabled by default] 
print_it_2(a, b, c) 
^ 
xyz.c: In function ‘print_it_2’: 
xyz.c:1:1: warning: type of ‘a’ defaults to ‘int’ [enabled by default] 
xyz.c:5:5: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration] 
    printf("%d %p %f\n", a, b, c); 
    ^
xyz.c:5:5: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default] 
$ 

同樣的警告與-std=c99給出。但是:

$ gcc -std=c89 -c xyz.c 
xyz.c: In function ‘print_it_2’: 
xyz.c:5:5: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default] 
    printf("%d %p %f\n", a, b, c); 
    ^
$ 

,並通過提供#include <stdio.h>,甚至會被平息。

ISO/IEC 9899:2011的6.9.1部分涵蓋了功能定義(它與C99中的章節號相同)。然而,這兩者都排除了C89必須允許的'隱含的int'行爲(因爲很多預先存在的代碼沒有聽說過規則。)然而,即使C11允許舊的樣式定義(完整型):

函數的定義:
聲明 - 符聲明符聲明列表選擇複合語句

聲明-1 IST:
聲明
申報表聲明

的「聲明列表選擇」是的函數聲明,這是函數體複合語句之間的類型列表。

C11還表示:

§6如果聲明符包括標識符列表,在聲明列表中的每個聲明應 具有至少一個聲明符,這些說明符應宣佈從 標識符列表僅標識符並且應該聲明標識符列表中的每個標識符。

我不能馬上找到我「的註釋C標準」一書,其中包含了左手頁面上的有用信息(1989/1990 C標準),並經常低於有用信息的副本每個傳播的右側頁面。這將允許我引用標準,但目前它是非常棒的。

C89在本段§6中的措辭不那麼嚴格。

至於爲什麼gcc不會拒絕不合時宜的書面舊式代碼,答案仍然是「向後兼容性」。這裏仍然有舊的代碼,它允許它在可能的時候進行編譯,除非通過使用-Werror將它的手仔細地綁在背後。

+0

但令我驚訝的是,即使使用-std = c11,gcc也會編譯它。當然,您始終可以將警告視爲錯誤,但這通常不適用於第三方代碼。您能否指出允許或禁止未申報參數的相關標準?如果確實被禁止,爲什麼gcc在被要求時不符合標準? –

相關問題