【C语言笔记初级篇】第三章:函数与递归
第三章:函數
(1)函數是什么
在計算機科學中,子程序是一個大型程序中的某部分代碼, 由一個或多個語句塊組成。它負責完成某項特定任務,而且相較于其他代碼,具備獨立性。一般會有輸入參數并有返回值,提供對過程的封裝和細節的隱藏。這些代碼通常被集成為軟件庫。
C語言中的函數分為庫函數和自定義函數。
(2)庫函數
A:什么是庫函數
我們知道,在學習C語言時,為了檢驗成果,我們總是會利用的“printf”這樣一個函數將結果打印到屏幕上,其實在C語言剛剛出現時,要想實現屏幕打印效果,是要自己定義一個打印函數的,這樣效率就顯得十分低下。所以在以后的C語言中,為了統一規定以及提高效率,會提供一些常用的,公用的函數以供我們使用。比如printf函數就是在標準輸入輸出這個庫函數下的一個函數
B:如何學習庫函數
第一點:學習庫函數的網站
學習庫函數的網站:Reference - C++ Reference
第二點:如何學習庫函數
學習庫函數當然不是要全部把庫函數記住,而是在編程過程中如果需要某種需求,進而聯想到是否庫函數中有能實現這種功能的函數。比如我么在操作字符串時,需要有一個字符串復制的需求,那么我們就去聯想字符串是str,復制是copy,而正好庫函數中有strcpy這樣一個函數能滿足我們的需求?,F在就利用上面的網站,去演示如何閱讀這樣的手冊
了解完之后,我們就可以寫出代碼了
(3)自定義函數
A:自定義函數
庫函數是不可能實現我們的全部需求的,所以很多時候需要我們自己去定義函數,實現某些功能
B:函數的組成
C:定義自己的函數
(4)函數的參數
A:引例:為什么不能交換成功
第一點:為什么不可以交換
如下:利用前面說過的自定義函數,寫一個交換兩個數的函數,并調用它
接下來:用調試的方法解釋為什么沒有交換成功
第二點:正確的交換方法
既然把值傳到函數后,函數自己開辟了自己的空間去操作自己的變量,導致了交換不成功。而之前我們說過利用指針可以直接操作內存,所以我們可以調用函數直接操作變量的內存,進而類似于一種“遠程操控”。
仍然用調試的方法解釋
B:形參和實參
實參:也即實際參數,是真實傳給函數的參數,叫實參。實參可以是:常量、變量、表達式、函數等。無論實參是何種類型的量,在進行函數調用時,它們都必須有確定的值,以便把這些值傳送給形參。
形參:也即形式參數,形式參數是指函數名后括號中的變量,因為形式參數只有在函數被調用的過程中才實例化(分配內存單元),所以叫形式參數。形式參數當函數調用完成之后就自動銷毀了。因此形式參數只在函數中有效。
綜上,這樣就完完全全揭示了為什么第一次的交換不能成功:形參實例化之后其實相當于實參的一份臨時拷貝。
(5)函數的調用
A:傳值調用
就像第一次的交換函數,函數的形參和實參分別占有不同的內存塊,對于形參的修改不會影響到實參
B:傳址調用
就像第二次的交換函數,將地址傳遞給函數,使得函數內部可以直接操縱函數外的變量
C:何時采用傳值,何時采用傳地址
如果不改變變量,就像要求兩個數的最大值,我只需讓函數返回誰大,并不去改變他們,那么就采用傳值;如果要改變變量,就像第二次交換函數,a和b的值是互換了,那么就需要傳地址。
(6)函數的嵌套調用和鏈式訪問
A:函數的嵌套調用
函數的嵌套調用說白了就是你調用我,我調用它
B:鏈式訪問
鏈式訪問就是把一個函數的返回值作為另一個函數的參數
這里要特別注意下面一個有趣的打印
(7)函數的聲明和定義
A:函數的聲明和定義
函數聲明:告訴編譯器我有一個函數,參數是什么,返回類型是什么,但是是否存在無關緊要。函數的聲要先聲明后使用,一般函數的聲明要放在頭文件中。
函數定義:函數的定義是指函數的具體實現
B:函數的聲明和定義的真正用途
這樣在一個文件里,先聲明后定義的方式,純屬脫褲子放屁
真正的使用方式,上述中調用的加法要放在其他模塊里,這個模塊包括一個頭文件,用于聲明函數;一個源文件,用于實現函數的功能,最后在主文件導入頭文件后調用
初學者不易理解這樣做的目的。舉個例子,要完成一個大的工程,比如說要寫出一個計算器,5個人完成,但是5個人不可能在一個文件里同時寫。所以就將任務發布下去,一個人寫加法,一個人寫減法,等等,最后把所有集合起來,就完成了這樣一項工作。
(8)遞歸
A:遞歸初識
第一點:何為遞歸
程序調用自身的編程技巧稱為遞歸( recursion)。 遞歸做為一種算法在程序設計語言中廣泛應用。 一個過程或函數在其定義或說明中有直接或間接調用自身的一種方法,它通常把一個大型雜的問題層層轉化為一個與原問題相似的規模較小的問題來求解,遞歸策略只需少量的程序就可描述出解題過程所需要的多次重復計算,大大地減少了程序的代碼量。 遞歸的主要思考方式在于:把大事化小。
第二點:最簡單的遞歸
第三點:遞歸會遇到的問題
像上面的那個遞歸,如果無休無止的下去,有一刻,程序會拋出錯誤“stackflow”,中文名意味棧溢出。我們的內存空間被劃分為棧區,堆區,靜態區,其中堆區是用來存放靜態變量,函數的,每一次遞歸就相當于每一次向內存申請空間,總有一顆,堆區耗盡,拋出錯誤。
B:案例說明
第一:接受一個整型值(無符號),按照順序打印它的每一位。 例如: 輸入:1234,輸出 1 2 3 4
代碼如下
初學者,在理解這段代碼時是有一定困難,先通過畫圖,來輔助理解
以輸入123為例:當調用n=1時,發現不滿足條件,這個遞歸的函數結束,返回上一遞歸的函數,上一遞歸的函數就是將n=12傳遞給它的那個函數,當返回過去,調用執行完畢,if判斷完畢,n=12,接著執行下一步輸出,就輸出了12%10=2,依次類推。
也可以這樣說遞歸其實保存了一個狀態,當被遞歸的函數結束后,返回到上一次層函數時,所有的狀態就回到了那一層函數時的狀態。
第二:不允許使用庫函數,使用遞歸求字符串長度
先用普通的方式實現,其實也就是“strlen”庫函數的實現原理,代碼如下
這里需要提前說明的是,將數組傳遞函數,傳進去的是該數組首元素的地址,所以形參是一個指針
接著使用遞歸的方式,代碼如下
遞歸講求一個大事化小的原則,試想如果字符串里什么都沒有,只有結束標記,那么直接返回沒有即可。如果指針掃描到的不是結束標記,那么就表示字符串長度起碼為1,那么它的字符長度應該是1加上指針移動到下一個元素上并調用該函數的返回值,也就是遞歸
第三:使用遞歸的方式求n的階乘
第四:求第n個斐波那契數
long long Fibonacci_Solution1(unsigned int n) {if(n <= 0)return 0;if(n == 1)return 1;return Fibonacci_Solution1(n - 1) + Fibonacci_Solution1(n - 2); }C:遞歸的大問題
在斐波那契數列那個案例中,當求前幾個數列時速度很快,但隨輸入的數字越大,我們發現計算機算的很慢,有時連人工的速度都比不上。
可以發現在算數列時,每次遞歸,有很多步驟都是重復的,這些函數之間似乎不聯系,有些數據不公用,就是我算我的,我完成我的任務,所以這也是遞歸很大的一個問題
為了驗證這一點,我們定義一個全局變量count,計算第40個斐波那契數,我們想看一下,計算第十40斐波那契數的時候,第3個斐波那契數被重復計算了幾次
可以發現count達到了驚人的三千多萬次,也就說明其實這三千多萬次是沒必要重復計算的,更何況這只是第三次的。
D:問題的解決方案
上述斐波那契數列是可以寫成非遞歸的方式的
新人創作打卡挑戰賽發博客就能抽獎!定制產品紅包拿不停!總結
以上是生活随笔為你收集整理的【C语言笔记初级篇】第三章:函数与递归的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 自定义组件随着手指自动画
- 下一篇: (软件工程复习核心重点)第三章需求分析-