C語言基礎--函數(指針函數、函數指針、回調函數、遞歸函數)

函數

1. 函數的定義和聲明

  • 1.函數定義

    <數據類型> <函數名稱> (<形參列表>)
    {
        語句序列;
        return (<表達式> );
    }
    
  • 若缺省返回值類型則會默認爲int型

  • 函數名即函數的入口地址

函數的聲明

​ 即將函數的相關信息告訴編譯器,如此才能使用函數。

  • 函數必須先聲明才能使用

2. 函數的參數傳遞

  1. 值傳遞
    • 形參變量只有在被調用是才分配內存空間,調用結束即釋放。
    • 實參和形參在數量、類型、書序上必須保證嚴格的一致,否則會發生類型不匹配的錯誤
    • 值傳遞的方式,不會影響實參的值。
  2. 地址傳遞方式
    • 地址傳遞的方式,被調用函數中可以改變實參的值。
  • 3.全局變量傳參
    • 因爲全局變量對所有函數可見,所以任何一個函數給變了全局變量都會對其他使用該全局變量的函數產生影響。

3. 函數的返回值

  • 函數的返回值只能通過return語句返回主調函數。
  • 函數返回值的類型應該與函數定義時的一致,否則會發生強制類型轉換。
  • 缺省返回值類型爲int
  • 函數的返回值不是由return返回去的,而是操作系統通過寄存器返回的,故return後可不跟參數。return可單獨使用可用於終止程序的執行

4. 函數與數組

  • 1.傳遞數組

    當形參是數組形式時,本質是同級別的指針。如

    int fun(int a[],int *p,char a);
    等價於;
    int fun(int *q,int *p,char a);
    
  • 2.傳遞指針

    main函數的參數

  • 其實,main函數也可以帶參數的,其參數是通過命令行傳入,其形式如下:

    int main(int argc,char *argv[])
     也可以寫爲:
    int main(int argc,char **argv)
    

5. 指針函數

  • 若一個函數的返回值爲一個指針,則這個函數就叫做指針函數

  • 指針函數返回的指針在主調函數中必須是有效的,否則會報錯。

  • 指針函數不能返回局部指針變量。但可以返回static修飾的靜態指針變量,因爲局部變量存儲在棧區,函數結束時就會釋放,而靜態變量存儲在靜態存儲區,直到程序結束纔會被被釋放。

    如:下面這段代碼中指針是不能被返回的,這樣寫是會報錯的。

    char *fun(void)
    {
        char s[]={0};
        strcpy(s,"welcome");
        
        return s;
    }
    
  • 可以返回指向字符串常量的指針,因爲字符串常量和靜態變量類似,都是程序結束時,才釋放內存,因此指針可以返回一個字符串常量的地址。如;

    char* fun(void)
    {
        char *str = "hello";
        return str;
    }
    
  • 指針函數可以返回一個堆區上面的空間,如:

    char *fun(void)
    {
        char *p = (char *)malloc(18);
        return p;
    }
    

總結:

​ 指針函數不可以返回局部變量的地址,可以返回一下三種情況:

  1. 靜態變量的地址
  2. 字符串常量的地址
  3. 堆上的地址

6. 函數指針

  • 函數指針專門用來存放函數的地址

  • 函數指針的一半形式如下:

    <數據類型> (*<函數指針名稱>)(<參數說明列表>);

    注意和指針函數的區別,括號的作用是使得*先和指針變量結合。

  • 函數指針的形式要與所指向的函數的形式保持一致。

    Linux內核中經常用到一下定義方法:

typedef <數據類型> (*<函數指針名稱>) (<形參列表>)
  • 在函數指針變量說明前面加上typedef,就變成了函數指針類型。即聲明瞭一個函數指針的數據類型。

函數指針數組:

  • 函數指針數組是一個包含若干個函數指針變量的數組。定義形式爲:

    <數據類型> (*<函數指針數組名稱> [<大小>]) (<參數說明列表>)
    

    即該數組中可以存放多個同一類型函數的地址。

    舉一個函數指針的典型的例子,如內核中的信號註冊函數。

    #include <signal.h>
    typedef void (*sighandler_t)(int);
    sighander_t signal(int signum,sighandler_t handler);
    

    看起來是不是特別奇妙,很有啓發?

7. 遞歸函數

  1. 遞歸函數的定義

    • 所謂遞歸函數是指一個函數的函數體中直接或者間接調用了該函數自身。

      遞歸函數的調用的執行過程分爲兩個階段:

      • 遞推階段:從原問題出發,按遞歸公式從未知到已知,最終達到遞歸終止條件。
      • 迴歸階段:按遞歸終止條件求出結果,逆向逐步代入遞歸公式,迴歸到原問題求解。

      注意:必須具有遞歸終止條件,否則會進入無限遞歸。

  2. 函數調用機制

    • 函數可以嵌套調用,但是不能嵌套定義。這是遞歸能夠實現的理論基礎。
  3. 遞歸調用的條件

    • 需有完成函數任務的語句
    • 遞歸終止條件
    • 一個遞歸調用語句
    • 先測試,後遞歸調用
  4. 回調函數

    • 就是一個通過函數指針調用的函數。

    回調函數的機制:

    1. 定義一個回調函數;
    2. 提供函數的一方在初始化的時候,將回調函數的函數指針註冊給調用者
    3. 當特定時間發生的時候,調用者使用函數指針調用回調函數對事件進行處理。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章