1. 程式人生 > >【C深入】認清函式的真面目

【C深入】認清函式的真面目

函式的由來與好處


——以下摘自陳正衝《C語言深度剖析》

其實在組合語言階段,函式這個概念還是比較模糊的。組合語言的程式碼往往就是從入口開始一條一條執行,直到遇到跳轉指令(比如ARM 指令B 、BL 、BX 、BLX 之類)然後才跳轉到目的指令處執行。這個時候所有的程式碼僅僅是按其將要執行的順序排列而已。後來人們發現這樣寫程式碼非常費勁,容易出錯,也不方便。於是想出一個辦法,把一些功能相對來說能成為一個整體的程式碼放到一起打包,通過一些資料介面和外界通訊。這就是函式的由來。那函式能給我們帶來什麼好處呢?簡單來說可以概括成以下幾點: 
    1、降低複雜性:使用函式的最首要原因是為了降低程式的複雜性,可以使用函式來隱含資訊,從而使你不必再考慮這些資訊。 
    2、避免重複程式碼段:如果在兩個不同函式中的程式碼很相似,這往往意味著分解工作有誤。這時,應該把兩個函式中重複的程式碼都取出來,把公共程式碼放入一個新的通用函式中,然後再讓這兩個函式呼叫新的通用函式。通過使公共程式碼只出現一次,可以節約許多空間。因為只要在一個地方改動程式碼就可以了。這時程式碼也更可靠了。 
    3、限制改動帶來的影響:由於在獨立區域進行改動,因此,由此帶來的影響也只限於一個 或最多幾個區域中。 
    4、隱含順序:如果程式通常先從使用者那裡讀取資料,然後再從一個檔案中讀取輔助資料,在設計系統時編寫一個函式,隱含哪一個首先執行的資訊。 
    5、改進效能:把程式碼段放入函式也使得用更快的演算法或執行更快的語言(如彙編)來改進這段程式碼的工作變得容易些。 
    6、進行集中控制:專門化的函式去讀取和改變內部資料內容,也是一種集中的控制形式。 
    7、隱含資料結構:可以把資料結構的實現細節隱含起來。 
    8、隱含指標操作:指標操作可讀性很差,而且很容易引發錯誤。通過把它們獨立在函式中,可以把注意力集中到操作意圖而不是集中到的指標操作本身。 
    9、隱含全域性變數:引數傳遞。 
    

函式的意義

模組化程式設計


C語言中的模組化


面向過程的程式設計

面向過程是一種以過程為中心的程式設計思想

首先將複雜的問題分解為一個個容易解決的問題

分解過後的問題可以按照步驟一步步完成

函式是面向過程在C語言中的體現

解決問題的每個步驟可以用函式來實現

宣告和定義

程式中的宣告可以理解為預先告訴編譯器實體的存在,如:變數,函式,等等

程式中的定義明確指示編譯器實體的意義


宣告和定義並不相同!

例1:test.c

// global.c   
// int g_var = 0;

#include <stdio.h>

extern int g_var;   //宣告外部全域性變數int g_var

void f(int i, int j);   //宣告函式f

int main()
{
    int g(int x);   //宣告函式g
    
    g_var = 10;
    
    f(1, 2);
    
    printf("%d\n", g(3));   //聲明後的函式、變數就都合法了
    
    return 0;
}                       

void f(int i, int j)  //函式定義
{
    printf("i + j = %d\n", i + j);
}

int g(int x)  //函式定義
{
    return 2 * x + g_var;
}

函式引數
函式引數在本質上與區域性變數相同,都是在棧上分配空間

函式引數的初始值是函式呼叫時的實參值


例2:

int f(int i, int j)
{
    printf("%d, %d\n", i, j);
}
int main()
{
    int k = 1;
    
    f(k, k++);
    
    printf("%d\n", k);
    
    return 0;
}

編譯執行得疑問

函式引數的求值順序依賴於編譯器的實現!!!上例中f(k,k++)從右向左的話則能說通

C語言中大多數運算子對其運算元求值的順序都是依賴於編譯器的實現的!!!

程式中的順序點

程式中存在一定的順序點

順序點指的是執行過程中修改變數值的最晚時刻

在程式到達順序點的時候,之前所做的一切操作必須反映到後續的訪問中

C語言中的順序點

1.每個完整表示式結束時,即遇到;時

2.&&,||,?:,以及逗號表示式的每個運算物件計算之後

3.函式呼叫中對所有實際引數的求值完成之後(進入函式體之前)

例3:

int main()
{
    int k = 2;
    int a = 1;
    
    k = k++ + k++;   //後置的k++中的+1操作會等到遇到;才會執行,所以先是k=2+2,遇到;,兩個++才執行,輸出6。但是前置的++k會馬上執行生效不會等;。
    
    printf("k = %d\n", k);
    
    if( a-- && a )      //a--遇到&&,--操作馬上生效,後面的a的值會變為0,所以這個if不執行
    {
        printf("a = %d\n", a);
    }  
    return 0;
}

例2中的 f(k, k++),就是由於順序點的作用

函式的預設認定

函式定義時引數和返回值的預設型別為int