C語言函式隱式宣告(2)

2021-09-05 11:47:26 字數 3220 閱讀 6477

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的函式體而報錯。...