1 什麼是c語言的隱式函式宣告
在c語言中,函式在呼叫前不一定非要宣告。如果沒有宣告,那麼編譯器會自動按照一種隱式宣告的規則,為呼叫函式的c**產生彙編**。下面是乙個例子:
int main(int argc, char** argv)
單純的編譯上述源**,並沒有任何報錯,只是在鏈結階段因為找不到名為any_name_function的函式體而報錯。
[smstong@centos192 test]$ gcc -c main.c
[smstong@centos192 test]$ gcc main.o
main.o: in function `main':
main.c:(.text+0x15): undefined reference to `any_name_function'
collect2: ld 返回 1
之所以編譯不會報錯,是因為c語言規定,對於沒有宣告的函式,自動使用隱式宣告。相當於變成了如下**:
int any_name_function();
int main(int argc, char** argv)
2 帶來的問題
2.1 隱式宣告函式名稱恰好在鏈結庫中存在,但返回非int型別
前面給出的例子,並不會造成太大影響,因為在鏈結階段很容易發現存在的問題。然而下面這個例子則會造成莫名的執行時錯誤。
#include int main(int argc, char** argv)
gcc編譯鏈結
[smstong@centos192 test]$ gcc -c main.c
main.c: 在函式『main』中:
main.c:6: 警告:隱式宣告與內建函式『sqrt』不相容
[smstong@centos192 test]$ gcc main.o
執行結果
1.000000
編譯時會給出警告,提示隱式宣告與內建函式』sqrt』不相容。gcc編譯器在編譯時能夠自動在常用庫標頭檔案(內建函式)中查詢與隱式宣告同名的函式,如果發現兩者並不相同,則會按照內建函式的宣告原型去生成呼叫**。這往往也是程式設計師預期的想法。
上面的例子中隱式宣告的函式原型為:
int sqrt(int);
而對應的同名內建函式原型為:
double sqrt(double);
最終編譯器按照內建函式原型進行了編譯,達到了預期效果。然而gcc編譯器的這種行為並不是c語言的規範,並不是所有的編譯器實現都有這樣的功能。同樣的原始碼在vc++2015下編譯執行的結果卻是:
vc++編譯
warning c4013: 「sqrt」未定義;假設外部返回 int
執行結果
2884223.000000
顯然,vc++編譯器沒有沒有所謂的「內建函式」,只是簡單的按照隱式宣告的原型,生成呼叫sqrt函式的**。由於返回型別和引數型別的不同,導致錯誤的函式呼叫方式,產生莫名奇妙的執行時錯誤。
對著這種情況,由於返回型別的不同,兩種編譯器都可以給出警告資訊,至少能引起程式設計師的注意。而下面這種情況,則更加隱蔽。
2.2 隱式宣告函式名稱恰好在鏈結庫中存在,且返回int型別
測試**如下:
#include int main(int argc, char** argv)
此時,由於隱式宣告的函式原型與gcc的內建函式原型完全相同,所以gcc不會給出任何警告,結果也是正確的。
而vc++則仍然會給出警告:warning c4013: 「abs」未定義;假設外部返回 int。
無論如何,隱式宣告的函式原型與庫函式完全相同,所以鏈結執行都是沒有問題的。
下面,稍微改動一下**:
#include int main(int argc, char** argv)
gcc下編譯鏈結沒有任何報錯。
gcc編譯鏈結
[smstong@centos192 test]$ gcc -c main.c
[smstong@centos192 test]$ gcc main.o
可見,gcc的內建函式機制並不關心函式的引數,只是關心函式的返回值。
vc++編譯鏈結
warning c4013: 「abs」未定義;假設外部返回 int
雖然這個例子的執行結果都是正確的,但是這種正確是「碰巧」的,因為額外的函式引數並沒有影響到結果。這種偶然正確是程式中要避免的。
3 程式設計中注意事項
c語言的隱式函式宣告,給程式設計師帶來了各種困惑,給程式的穩定性帶來了非常壞的影響。不知道當初c語言設計者是如何考慮這個問題的?
* 為了避免這種影響,強烈建議程式設計師重視編譯器給出的關於隱式宣告的警告,及時通過包含必要的標頭檔案來消除這種警告。*
對於gcc來說,前面給出的那個abs(-1,2,3,4)的特殊例子,編譯器根本不會產生任何警告,只能靠程式設計師熟悉自己呼叫的每乙個庫函式了。
為了避免這種問題,在c語言的c99版本中,無論如何都會給出警告。如gcc使用c99編譯上述**。
gcc -std=c99編譯
[smstong@centos192 test]$ gcc -c main.c -std=c99
main.c: 在函式『main』中:
main.c:5: 警告:隱式宣告函式『abs』
而c++則更嚴格,直接拋棄了隱式函式宣告,對於未宣告函式的呼叫,將直接無法通過編譯。
g++編譯
[smstong@centos192 test]$ g++ main.c
main.c: in function 『int main(int, char**)』:
main.c:5: 錯誤:『abs』在此作用域中尚未宣告
vc++編譯(作為c++)
error c3861: 「abs」: 找不到識別符號
在函式強型別這一點上,c++確實比c更嚴格,更嚴謹。
---------------------
C語言函式隱式宣告(1)
這段時間,在看中心後台服務軟體原始碼時發現,有很多自定義函式未經 宣告卻能在主程式中被呼叫,主程式中沒有包括上述函式的標頭檔案,我在各個目錄中也找不到上述函式的標頭檔案。這就奇怪了,連使用標準庫函式printf 都要包括標準輸入輸出標頭檔案,何況是自定義函式?這個問題困擾了我很久。前天問中創公司奚鍾...
C語言中的隱式函式宣告
1 什麼是c語言的隱式函式宣告 在c語言中,函式在呼叫前不一定非要宣告。如果沒有宣告,那麼編譯器會自動按照一種隱式宣告的規則,為呼叫函式的c 產生彙編 下面是乙個例子 1 2 3 4 5 int main int argc,char argv 單純的編譯上述源 並沒有任何報錯,只是在鏈結階段因為找不...
C語言中的隱式函式宣告
在c89中,函式在呼叫前不一定非要宣告。如果沒有宣告,那麼編譯器會自動按照一種隱式宣告的規則,為呼叫函式的c 產生彙編 下面是乙個例子 int main int argc,char ar 單純的編譯上述源 並沒有任何報錯,只是在鏈結階段因為找不到名為any name function的函式體而報錯。...