一、函數的真面目android
程序 = 數據 + 算法 ==》C程序 = 數據 + 函數算法
模塊化程序設計:編程
C語言中的模塊化:ide
面向過程的程序設計模塊化
面向過程是一種以過程爲中心的編程思想 首先將複雜的問題分解爲一個個容易解決的問題this 分解事後的問題能夠按照步驟一步步完成spa 函數是面向過程在C語言中的體現設計 解決問題的每一個步驟能夠用函數來實現3d |
聲明和定義:
程序中的聲明可理解爲預先告訴編譯器實體的存在,如:變量,函數,等等 程序中的定義明確指示編譯器實體的意義 |
函數參數
函數參數在本質上與局部變量相同,都是在棧上分配空間 函數參數的初始值是函數調用時的實參值 函數參數的求值順序依賴於編譯器的實現! C語言中大多數運算符對其操做數求值的順序都是依賴於編譯器的實現的 |
程序中的順序點
程序中存在必定的順序點 順序點指的是執行過程當中修改變量值的最晚時刻 在程序達到順序點的時候,以前所作的一切操做必須反映到後續的訪問中 每一個完整表達式結束時 |
函數的缺省認定
C語言會默認沒有類型的函數參數爲int |
二、可變參數分析與宏分析
可變參數
C語言中能夠定義參數可變的函數 參數可變函數的實現依賴於stdarg.h頭文件 va_list變量與va_start, va_end和va_arg配合使用可以訪問參數值 |
可變參數的限制
可變參數必須從頭至尾按照順序逐個訪問 參數列表中至少要存在一個肯定的命名參數 可變參數宏沒法判斷實際存在的參數的數量 可變參數宏沒法判斷參數的實際類型 警告: va_arg中若是指定了錯誤的類型,那麼結果是不可預測的 |
函數和宏的對比
宏是由預處理直接替換展開的,編譯器不知道宏的存在 函數是由編譯器直接編譯的實體,調用行爲由編譯器決定 屢次使用宏會致使程序代碼量增長 函數是跳轉執行的,所以代碼量不會增長 宏的效率比函數要高,由於是直接展開,無調用開銷 函數調用時會建立活動記錄,效率不如宏 |
宏的優勢和缺點
宏的效率比函數稍高,可是其反作用巨大,容易出錯 函數存在實參到形參的傳遞,所以無任何反作用,可是函數須要創建活動對象,效率受影響 宏參數能夠是任何C語言實體 |
三、函數調用行爲
活動記錄
活動記錄是函數調用時用於記錄一系列相關信息的記錄 臨時變量域:用來存放臨時變量的值,如k++的中間結果 局部變量域:用來存放函數本次執行中的局部變量 機器狀態域:用來保存調用函數以前有關機器狀態的信息,包括各類寄存器的當前值和返回地址等; 實參數域: 用於存放函數的實參信息 返回值域: 爲調用者函數存放返回值 |
參數入棧
既然函數參數的計算次序是依賴編譯器實現的,那麼函數參數的入棧次序是如何肯定的呢? |
調用約定
當一個函數被調用時,參數會傳遞給被調用的函數,而返回值會被返回給調用函數。函數的調用約定就是描述參數是怎麼傳遞到棧空間的,以及棧空間由誰維護。 參數傳遞順序 從左到右依次入棧:__pascal,__fastcall 調用堆棧清理 調用者清除棧。 被調用函數返回後清除棧 |
四、函數遞歸與函數設計技巧
遞歸概述
遞歸是數學領域中概念在程序設計中的應用 遞歸是一種強有力的程序設計方法 遞歸的本質爲函數內部在適當的時候調用自身 C遞歸函數有兩個主要的組成部分: |
函數設計技巧
不要在函數中使用全局變量,儘可能讓函數從意義上是一個獨立的功能模塊 參數名要可以體現參數的意義 void str_copy (char *str1, char *str2); void str_copy (char *str_dest, char *str_src); 若是參數是指針,且僅做輸入參數用,則應在類型前加const,以防止該指針在函數體內被意外修改 void str_copy (char *str_dest, const char *str_src); 不要省略返回值的類型,若是函數沒有返回值,那麼應聲明爲void類型 在函數體的「入口處」,對參數的有效性進行檢查,對指針的檢查尤其重要 函數體的規模要小,儘可能控制在80行代碼以內 有時候函數不須要返回值,但爲了增長靈活性,如支持鏈式表達,能夠附加返回值 函數名與返回值類型在語義上不可衝突 char c; if(EOF == c) } |