C語言中的隱式函式宣告

2021-10-07 12:09:27 字數 3887 閱讀 6427

在c89中,函式在呼叫前不一定非要宣告。如果沒有宣告,那麼編譯器會自動按照一種隱式宣告的規則,為呼叫函式的c**產生彙編**。下面是乙個例子:

int

main

(int argc,

char

** ar**)

單純的編譯上述源**,並沒有任何報錯,只是在鏈結階段因為找不到名為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

之所以編譯不會報錯,是因為c89規定,對於沒有宣告的函式,自動使用隱式宣告。相當於變成了如下**:

int

any_name_function()

;int

main

(int argc,

char

** ar**)

引用自

那麼函式宣告到底有宣告作用呢?

其實函式宣告的作用是讓編譯器幫你檢查函式呼叫時有沒有錯誤。比如引數的數量是否正確,如果呼叫函式時候少傳入乙個引數,並且沒有宣告該函式,編譯器無法知道你呼叫是否正確,只會提示乙個警告。很多人會忽略警告,導致最後程式執行時出現異常。

為什麼如果不宣告函式,編譯器發現不了錯誤?

編譯器在編譯過程中依次生成與原始檔對應的可重定位目標檔案(.o),每個原始檔中呼叫的函式在鏈結前都是以符號的形式體現在.o檔案中。在編譯過程中不會去檢查某個函式的形式,因為函式引數是通過暫存器和壓棧來處理的,直接把函式翻譯成符號,編譯器是不知道關於函式引數的資訊的,最後交給聯結器把符號翻譯成位址。所以鏈結的時候只要能找到對應得符號就不會報錯。

從現代c語言的角度來說,任何識別符號(除了goto的label以及main()的main)在使用之前都一定要宣告,函式也是如此。

理論上這是必須的,但某些編譯器為了遷就以前**的一些寫法放鬆了這個要求。所以還是應該寫,儘管看起來貌似不是必須的。

較新的c規範(c99、c11)是不允許不宣告直接用的。只是gcc預設還允許implicit function declaration功能。

前面給出的例子,並不會造成太大影響,因為在鏈結階段很容易發現存在的問題。然而下面這個例子則會造成莫名的執行時錯誤。

#include

intmain

(int argc,

char

** ar**)

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函式的**。由於返回型別和引數型別的不同,導致錯誤的函式呼叫方式,產生莫名奇妙的執行時錯誤。

對著這種情況,由於返回型別的不同,兩種編譯器都可以給出警告資訊,至少能引起程式設計師的注意。而下面這種情況,則更加隱蔽。

測試**如下:

#include

intmain

(int argc,

char

** ar**)

此時,由於隱式宣告的函式原型與gcc的內建函式原型完全相同,所以gcc不會給出任何警告,結果也是正確的。

而vc++則仍然會給出警告:warning c4013: 「abs」未定義;假設外部返回 int。

無論如何,隱式宣告的函式原型與庫函式完全相同,所以鏈結執行都是沒有問題的。

下面,稍微改動一下**:

#include

intmain

(int argc,

char

** ar**)

gcc編譯鏈結

[smstong@centos192 test]$ gcc -c main.c

[smstong@centos192 test]$ gcc main.o

沒有任何警告,可見,gcc的內建函式機制並不關心函式的引數,只是關心函式的返回值。

vc++編譯鏈結

warning c4013: 「abs」未定義;假設外部返回 int
雖然這個例子的執行結果都是正確的,但是這種正確是「碰巧」的,因為額外的函式引數並沒有影響到結果。這種偶然正確是程式中要避免的。

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 什麼是c語言的隱式函式宣告 在c語言中,函式在呼叫前不一定非要宣告。如果沒有宣告,那麼編譯器會自動按照一種隱式宣告的規則,為呼叫函式的c 產生彙編 下面是乙個例子 1 2 3 4 5 int main int argc,char argv 單純的編譯上述源 並沒有任何報錯,只是在鏈結階段因為找不...

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

這段時間,在看中心後台服務軟體原始碼時發現,有很多自定義函式未經 宣告卻能在主程式中被呼叫,主程式中沒有包括上述函式的標頭檔案,我在各個目錄中也找不到上述函式的標頭檔案。這就奇怪了,連使用標準庫函式printf 都要包括標準輸入輸出標頭檔案,何況是自定義函式?這個問題困擾了我很久。前天問中創公司奚鍾...

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

1 什麼是c語言的隱式函式宣告 在c語言中,函式在呼叫前不一定非要宣告。如果沒有宣告,那麼編譯器會自動按照一種隱式宣告的規則,為呼叫函式的c 產生彙編 下面是乙個例子 int main int argc,char argv 單純的編譯上述源 並沒有任何報錯,只是在鏈結階段因為找不到名為any nam...