在C語言程序開發中,我們常常使用一些庫函數(例如 printf,sin 等函數)。事實上,實際需求通常是非常複雜的,只依靠現有的函數很難完成。這就需要自己定義 C語言函數。

可以把一個函數看作是一塊積木,各種各樣的積木才能堆積成複雜的建築。只有幾種積木,能完成的建築太有限了,如果能夠自己造各種各樣的積木,再複雜的建築也能完成。

C語言函數的結構是固定的

在我之前的文章裏介紹過,C語言作為編程語言,是一種形式語言,具有固定的格式結構和符號:

返回值類型 函數名(參數列表)

{ 語句列表}

注意函數名在相應可見域內要保持唯一性,例如,在 main 函數所在文件裏定義的函數名不能也為 main。再囉嗦一下 main 函數,主要是因為它是默認的入口函數,比較特殊。

  1. Old Style C的風格的 main 函數定義寫成 main(){…} 形式,不寫返回值類型也不寫參數列表。
  2. Old Style C規定不寫返回值類型就表示返回int型,不寫參數列表就表示參數類型和個數沒有明確指出。這種寬鬆的規定會導致很多複雜的Bug產生,現在的 C 也保持了這種寫法的兼容,但是鑒於它可能會產生 bug,還是不要這樣寫。
  3. 其實系統在調用 main 函數時是傳參數的,所以 main 函數最標準的形式應該是 int main(int argc, char * argv[]),這種參數傳遞在 linux 下使用起來非常方便,以後用到了再詳細解釋。
  4. C標準也規定了 int main(void) 這種形式,如果不使用系統傳進來的兩個參數也可以寫成這種形式。但除了這兩種形式之外,以其它形式定義main函數都是錯誤的或不可移植的。

自定義函數

在瞭解了自定義函數的一些限制後,很容易就可以寫出如下代碼。下面的代碼中,我們定義了 printA 函數,它的參數為空,也沒有返回值,僅僅是列印出一個字母 「A」。

我們在 codeblocks 裏執行它,發現程序在控制檯中列印出了字母 A。

關於 codeblocks 的安裝和使用,可點擊我的主頁,查看相關文章。

以後如果想列印 A,只需要執行 printA 函數就可以了。更進一步的,我們自己定義的 printA 函數也可以當做系統函數(例如)一樣嵌套使用,請看如下代碼:

我們又新定義了一個函數 print2A,它的功能就是在控制檯列印兩個 A。在 codeblocks 裏執行,發現與預期一致。

體會

從上面的簡單例子中,可以總結出以下信息:

  • 同一個函數可以調用多次。
  • 可以用一個函數去調用另一個函數,第二個函數可以去調用第三個函數。
  • 調用自定義的函數,是通過我們取的名字調用的,例如 main 函數中調用 print2A();
  • 函數可以使代碼更簡潔,原本列印兩個 A 需要兩行代碼,現在只需要 print2A 一行就可以了。

你可能會說,我定義 print2A 就用了 5 行啊,哪裡簡潔了。可是,以後只要遇到需要列印兩個 A 的地方,我都可以調用 print2A,如果有 10 個地方需要列印兩個 A 呢?另外,如果有一天,我想在列印兩個 A 的時候不換行,那我只需要修改 printA,把裡面的 「
」刪掉就可以了,而不用去找那 10 個需要列印兩個 A 的地方逐個修改。

讀代碼和讀文章不一樣,按從上到下從左到右的順序讀代碼未必是最好的。比如上面的例子,按順序應該是先看 printA 再看 print2A 再看 main。不過,如果換一個角度,按代碼的執行順序來讀也許會更好:

因為 main 是入口函數,所以第一句要執行的就是 print2A 函數,那它是什麼功能呢?於是目光移到 print2A 函數,它的第一句是 printA 函數,於是進入 printA 函數。奧,知道了 print2A 先要列印一個 A,然後又要執行 printA 函數,又列印了一個 A。這下知道了,main 函數裏的第一句要列印兩個 A。

實際上,在很多大型項目中,要熟悉它的代碼結構,第一件事往往就是找到入口函數。

歡迎在評論區一起討論,質疑。文章都是手打原創,每天最淺顯的介紹C語言、linux等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦。


一共是兩個問題,我來簡單說一下。

怎樣定義自己的函數

設計:確定函數的功能、參數、返回值

實現:函數原型、函數的實現

舉個例子,Fibonacci函數:

F(1)=1, F(0)=1,F(N)=F(N-1)+F(N-2)

功能:計算Fibonacci函數值

參數:無符號整數N

返回值:無符號整數F(N)

代碼如下:

unsigned int fib(unsigned int n)

{ if(n==1 || n==0) return 1; return fib(n-1)+fib(n-2);}

通過遞歸調用可返回Fibonacci函數結果。

為什麼要定義函數

主要有幾方面作用:

  1. 簡化演算法:如例子中的Fibonacci函數求值,很適合用函數方式實現,如果用循環實現,複雜度會高很多;

  2. 方便分工:函數有清晰的可測試介面,通過函數方便多人之間分工;

  3. 方便維護代碼:例如替換函數的實現,而不影響函數介面,比平鋪的代碼維護容易

會不會麻煩呢?

如果自己對要編寫的代碼理解清晰,定義幾個函數完全不會麻煩;如果自己對要編寫的代碼理解不清晰,定義函數的過程正好方便自己整理思路。


推薦閱讀:
相關文章