在預標準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
(因此char
或short
)的任何類型被提升爲int
,並且float
被提升爲double
(略微簡化)。因此,在致電printit(a, ch)
中,實際傳遞的值爲double
和int
。在函數內部,代碼認爲a
是int
(具體性,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
將它的手仔細地綁在背後。
您正在使用K&R C的默認int行爲,並向編譯器說謊,因此您所期望的只是未定義的行爲。 –
你使用什麼C編譯器? –
g ++'作爲代碼塊中的默認設置 – user3392464