對c語言中static函數的理解


  先看看前兩篇博客:個人對頭文件的理解對聲明和定義的理解 

  static 函數只在定義該static函數的cpp中可見,在其他cpp中是不可見的。

  舉個例子,我建立了一個project,該project中有三個文件,一個頭文件,和兩個cpp文件。文件內容如下:

  在build該project時會出錯,錯誤提示是fun()函數聲明了卻未定義。

  為什么編譯器沒有找到fun()函數的定義呢?我明明在Source.cpp中定義了fun()函數呀。

  原因很簡單:static函數的作用域很小只在本cpp中有效而非在整個project中都有效。所以在main.cpp中無法找到fun()函數的定義。

  根據此特性,我們可以進行一些看似行不通的操作,比如下面這個:

 

  咦,居然build成功了,這是為啥?我之前明明說過每個函數至多只能定義一次,這兒明明定義了“兩次”。

  我們先來看看最后輸出的結果是啥?是“hello“還是”world“?

 

  答案是“world“,即main()函數中所使用的fun()函數采用的是在main.cpp中fun()函數的定義。

  根據剛才的結論,由於static函數的作用域只在本cpp中,因此Source.cpp和main.cpp中的fun()函數的作用域並沒有沖突。之前所說的函數至多只能被定義一次,實際上完整的說法應該是:在同一個作用域下,函數至多被定義一次。道理正如:一個世界上不存在兩片相同的樹葉,但是在另一個平行世界中,卻可能存在着和我們世界相同的樹葉。(要想知道根本原因的話還是得看看csapp里面關於link的這部分內容)。

  main.cpp中為fun()函數找到的定義自然是main.cpp中fun()的定義,而非Source.cpp中fun()函數的定義,因此,最后輸出為“world“就可以解釋的通了。

 

  下面討論兩種情況

  一、若是將static函數的完整定義寫在頭文件中會怎么樣?結果是每個包含該頭文件的cpp都可以使用該函數的定義。

 

  二、若是將非static函數的完整定義寫在頭文件中會怎么樣?結果是:若有多個cpp文件包含該頭文件,在link時,會因該函數被重復定義而失敗。

 

那么static函數的真正用法是啥?

    先說說頭文件的作用:頭文件的作用實際上就是聲明接口(函數),包含該頭文件的cpp(用戶)可以調用頭文件中所聲明的接口(函數)。

    前面說過,static函數的定義只在定義該函數的cpp中有效。下面討論兩種情況:

  第一種情況:某 static fun()函數在a.h中被聲明,然后a.cpp包含了a.h並對static fun()函數作出了定義。此時有一個b.cpp出現了,它也包含了a.h,然后它就看到了fun()函數,它以為fun()函數是別人已經寫好的接口,然后它就調用fun()函數,結果會如何?link失敗,情況與上面的例子相同。因此static函數的聲明不應該放在頭文件中。

  第二種情況:將static函數的定義放在頭文件中,build會出問題么?不會,但是有必要這么做么?沒必要。這樣做的效果是讓每個包含該頭文件的cpp文件都能夠使用該接口(函數),既然目的是讓每個cpp文件都能夠使用該接口,就沒必要將該函數設置為static函數了。將其設置為非static函數,在某個頭文件中聲明,然后隨便在某個cpp文件中定義不是更好么?如果將static函數定義在頭文件中,會增加compile的工作量,因為每個包含該頭文件的cpp文件都需要對該函數進行編譯。因此static的定義不應該放在頭文件中。

  那么只剩下一種選擇:static函數的聲明和定義都放在cpp文件中。

使用static函數的正確姿勢:

  其實static函數的真正作用在於數據隱藏(類似與c++類中的private屬性),因為它只在定義它的cpp文件中是可見的嘛。

  比如說有這么一種情況,庫的制作者向用戶提供了兩個接口interface1()和interface2(),這兩個函數都調用了interfaceBase()這個函數,但是制作者並不想將interfaceBase()展示給用戶(可能是怕用戶用interfaceBase()函數搞破壞吧),同時interfaceBase()這個函數又只在定義這兩個函數的cpp文件中使用。那么應該怎樣做呢?

  首先肯定應該將interface1()和interface2()的聲明放在某頭文件中,為了提供接口嘛,然后在定義這兩個個接口的cpp文件中定義一個static屬性的interfaceBase()函數。最終如圖所示:

 

  但是我將一個非static函數的聲明和定義都放在cpp文件中也能夠達到隱藏接口的目的呀,那么使用static函數有什么優勢呢?

  這又得從作用域說起了,普通函數的作用域是整個project,而static函數的作用域僅限於本cpp。如果你在兩個cpp文件中都定義了fun()函數,那么肯定會產生link錯誤。但是你如果在兩個cpp文件中定義的是static fun()函數,那么就能避免link錯誤。

  因此使用static函數可以使函數重名。

  綜上:如果某個函數只在某個cpp中使用,並且不希望將這個函數暴露給外界,那么就應該將它定義為static函數,定義在cpp中。

 

補充一種情況:

  如果在a.cpp里面定義static void fun(),在b.cpp里面定義了void fun();編譯時不會產生鏈接沖突,當在a,cpp里面調用fun()函數時,調用的是a.cpp中的static void fun(),而非b,cpp中void fun()。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM