附3.函數(shù)指針

一.函指針概述

函數(shù)的作用:完成某一特定功能的代碼塊屡江。

指針的作用:一種特殊的變量,用來保存地址值撩穿,某類型的指針指向某類型的地址功舀。

下面定義了一個(gè)求兩個(gè)數(shù)最大值的函數(shù):
int maxValue (int a, int b) {
        return a > b ? a : b; 
} 

這段代碼編譯后生成的CPU指令存儲(chǔ)在代碼區(qū),而這段代碼其實(shí)是可以獲取其地址的毫别,而其地址就是函數(shù)名娃弓,我們可以使用指針存儲(chǔ)這個(gè)函數(shù)的地址——函數(shù)指針。
函數(shù)指針其實(shí)就是一種特殊的指針——指向一個(gè)函數(shù)的指針拧烦。在很多高級(jí)語言中忘闻,它的思想是很重要的,尤其是它的“回調(diào)函數(shù)”恋博。

二.函數(shù)指針定義與使用

任何變量定義都包含三部分: 變量類型 + 變量名 = 初值齐佳,那么定義一個(gè)函數(shù)指針,首先我們需要知道要定義一個(gè)什么樣的函數(shù)指針(指針類型)债沮,那么問題來了炼吴,函數(shù)的類型又是什么呢?我們繼續(xù)分析這段代碼:

int maxValue (int a, int b) {
    return a > b ? a : b;
}    

這個(gè)函數(shù)的類型是有兩個(gè)整型參數(shù)疫衩,返回值是個(gè)整型硅蹦。對(duì)應(yīng)的函數(shù)指針類型:

int (*) (int a, int b)

對(duì)應(yīng)的函數(shù)指針定義:

int (*p)(int x, int  y);   

參數(shù)名可以去掉,并且通常都是去掉的。這樣指針p就可以保存函數(shù)類型為兩個(gè)整型參數(shù)童芹,返回值是整型的函數(shù)地址了涮瞻。

int (*p)(int, int);

通過函數(shù)指針調(diào)用函數(shù):

int (*p)(int, int) = NULL;
p = maxValue;
p(20, 45);

三.回調(diào)函數(shù)

現(xiàn)在我們有這樣一個(gè)需求:實(shí)現(xiàn)一個(gè)函數(shù),將一個(gè)整形數(shù)組中比50大的打印在控制臺(tái)假褪,我們可能這樣實(shí)現(xiàn):

void compareNumberFunction(int *numberArray, int count, int compareNumber) { 
      for (int i = 0; i < count; i++) { 
           if (*(numberArray + i) > compareNumber) { 
                printf("%d\n", *(numberArray + i)); 
           }
       }
 }

 int main() {
 int numberArray[5] = {15, 34, 44, 56, 64};
 int compareNumber = 50;
 compareNumberFunction(numberArray, 5, compareNumber);
 return 0;
 } 

這樣實(shí)現(xiàn)是沒有問題的署咽,然而現(xiàn)在我們又有這樣一個(gè)需求:實(shí)現(xiàn)一個(gè)函數(shù),將一個(gè)整形數(shù)組中比50小的打印在控制臺(tái)生音。"What the fuck!"對(duì)于提需求者宁否,你可能此時(shí)的心情是這樣:


然而回到現(xiàn)實(shí),這種需求是不可避免的缀遍,你可能想過復(fù)制粘貼慕匠,更改一下判斷條件,然而作為開發(fā)者域醇,我們要未雨綢繆台谊,要考慮到將來可能添加更多類似的需求,那么你將會(huì)有大量的重復(fù)代碼歹苦,使你的項(xiàng)目變得臃腫青伤,所以這個(gè)時(shí)候我們需要冷靜下來思考,其實(shí)這兩個(gè)需求很多代碼都是相同的殴瘦,只要更改一下判斷條件即可狠角,而判斷條件我們?nèi)绾巫兊酶屿`活呢?這時(shí)候我們就用到回調(diào)函數(shù)的知識(shí)了蚪腋,我們可以定義一個(gè)函數(shù)丰歌,這個(gè)函數(shù)需要兩個(gè)int型參數(shù),函數(shù)內(nèi)部實(shí)現(xiàn)代碼是將兩個(gè)整形數(shù)字做比較屉凯,將比較結(jié)果的bool值作為函數(shù)的返回值返回出來立帖,以大于被比較數(shù)字的情況為例:

BOOL compareGreater(int number, int compareNumber) {
         return number > compareNumber; 
} 

同理,小于被比較的數(shù)字函數(shù)定義如下:

BOOL compareLess(int number, int compareNumber) { 
         return number < compareNumber; 
} 

接下來悠砚,我們可以將這個(gè)函數(shù)作為compareNumberFunction的一個(gè)參數(shù)進(jìn)行傳遞(沒錯(cuò)晓勇,函數(shù)可以作為參數(shù)),那么我們就需要一個(gè)函數(shù)指針獲取函數(shù)的地址灌旧,從而在compareNumberFunction內(nèi)部進(jìn)行對(duì)函數(shù)的調(diào)用绑咱,于是,compareNumberFunction函數(shù)的定義變成了這樣:

void compareNumberFunction(int *numberArray, int count, int compareNumber, BOOL (*p)(int, int)) { 
     for (int i = 0; i < count; i++) { 
            if (p(*(numberArray + i), compareNumber)) {
                    printf("%d\n", *(numberArray + i)); 
            }
      }
} 

具體使用時(shí)代嗎如下:

int main() { 
         int numberArray[5] = {15, 34, 44, 56, 64}; 
         int compareNumber = 50;
         // 大于被比較數(shù)字情況: 
        compareNumberFunction(numberArray, 5, compareNumber,compareGreater); 
        // 小于被比較數(shù)字情況: 
        compareNumberFunction(numberArray, 5, compareNumber, compareLess); 
        return 0; 
} 

根據(jù)上述案例枢泰,我們可以得出結(jié)論:函數(shù)回調(diào)本質(zhì)為函數(shù)指針作為函數(shù)參數(shù)描融,函數(shù)調(diào)用時(shí)傳入函數(shù)地址,這使我們的代碼變得更加靈活衡蚂,可復(fù)用性更強(qiáng)窿克。

四.動(dòng)態(tài)排序

需求: 有30個(gè)學(xué)生需要排序
按成績排
按年齡排

這種無法預(yù)測的需求變更骏庸,就是我們上文說的動(dòng)態(tài)場景,那么解決方案就是函數(shù)回調(diào):

typedef struct student{ 
     char name[20]; 
     int age; 
     float score;
}Student; 
//比較兩個(gè)學(xué)生的年齡 
BOOL compareByAge(Student stu1, Student stu2) { 
       return stu1.age > stu2.age ? YES : NO; 
}
 //比較兩個(gè)學(xué)生的成績 
BOOL compareByScore(Student stu1, Student stu2) {
        return stu1.score > stu2.score ? YES : NO; 
}
void sortStudents(Student *array, int n, BOOL(*p)(Student, Student)){ 
      Student temp; 
      int flag = 0; 
      for (int i = 0; i < n - 1 && flag == 0; i++) { 
          flag = 1; 
          for (int j = 0; j < n - i - 1; j++) { 
             if (p(array[j], array[j + 1])) {
               temp = array[j]; 
               array[j] = array[j + 1]; 
               array[j + 1] = temp;
               flag = 0; 
             }
           }
      }
 }
 int main() { 
       Student stu1 = {"小明", 19, 98}; 
       Student stu2 = {"小紅", 20, 78}; 
       Student stu3 = {"小白", 21, 88};
       Student stuArray[3] = {stu1, stu2, stu3}; 
       sortStudents(stuArray, 3, compareByScore); 
       return 0;
 }

五.函數(shù)指針作為函數(shù)返回值

沒錯(cuò)年叮,既然函數(shù)指針可以作為參數(shù)具被,自然也可以作為返回值。

需求:定義一個(gè)函數(shù)只损,通過傳入功能的名稱獲取到對(duì)應(yīng)的函數(shù)硬猫。


整理一下發(fā)現(xiàn),然后我們分析下需求改执,當(dāng)前我們需要定義一個(gè)叫做findFunction的函數(shù),這個(gè)函數(shù)傳入一個(gè)字符串之后會(huì)返回一個(gè)int (*)(int, int)類型的函數(shù)指針坑雅,那么我們這個(gè)函數(shù)的聲明是不是可以寫成這樣呢辈挂?

int (*)(int, int) findFunction(char *); 
//這看起來很符合我們的理解
//然而,這并不正確
//編譯器無法識(shí)別兩個(gè)完全并行的包含形參的括號(hào)(int, int)和(char *)
//真正的形式其實(shí)是這樣:
int (*findFunction(char *))(int, int); 

這種聲明從外觀上看更像是臉滾鍵盤出來的結(jié)果裹粤,現(xiàn)在讓我們來逐步的分析一下這個(gè)聲明的組成步驟:

findFunction是一個(gè)標(biāo)識(shí)符

findFunction()是一個(gè)函數(shù)

findFunction(char *)函數(shù)接受一個(gè)類型為char *的參數(shù)

*findFunction(char *)函數(shù)返回一個(gè)指針

(*findFunction(char *))()這個(gè)指針指向一個(gè)函數(shù)

(*findFunction(char *))(int, int)指針指向的函數(shù)接受兩個(gè)整形參數(shù)

int (*findFunction(char *))(int, int)指針指向的函數(shù)返回一個(gè)整形

現(xiàn)在我們的分析已經(jīng)完成了终蒂,編譯器可以通過了,現(xiàn)在程序員瘋了遥诉,這對(duì)我們來說就像鯡魚罐頭一樣難以下咽拇泣,那么我們是不是有更好的書寫方式呢?(老司機(jī)友情提示:typedef)

最終代碼演變成了這樣:

// 重定義函數(shù)指針類型 
typedef int (*FUNC)(int, int); 
// 求最大值函數(shù) 
int maxValue(int a, int b) { 
   return a > b ? a : b; 
} 
// 求最小值函數(shù)
int minValue(int a, int b) { 
   return a < b ? a : b; 
}
// findFunction函數(shù)定義
FUNC findFunction(char *name) { 
   if (0 == strcmp(name, "max")) { 
      return maxValue; 
   } else if (0 == strcmp(name, "min")) { 
      return minValue; 
   } 
   printf("Function name error"); 
   return NULL; 
} 
int main() {
   int (*p)(int, int) = findFunction("max");
   printf("%d\n", p(3, 5));
   int (*p1)(int, int) = findFunction("min"); 
   printf("min = %d\n", p1(3, 5)); 
   return 0; 
} 

到了這里矮锈,函數(shù)指針的內(nèi)容已經(jīng)結(jié)束了霉翔,有的同學(xué)還有可能困惑,為什么我要以函數(shù)去獲取函數(shù)呢苞笨,直接使用maxValue和minValue不就好了么债朵,其實(shí)在以后的編程過程中,很有可能maxValue和minValue被封裝了起來瀑凝,類的外部是不能直接使用的序芦,那么我們就需要這種方式,如果你學(xué)習(xí)了Objective-C你會(huì)發(fā)現(xiàn)粤咪,所有的方法調(diào)用的實(shí)現(xiàn)原理都是如此谚中。


補(bǔ)充:strcmp

原型:int strcmp(const char *s1, const char *s2);
頭文件:#include <string.h>

功能:用來比較兩個(gè)字符串
參數(shù):s1、s2為兩個(gè)進(jìn)行比較的字符串
返回值:若s1寥枝、s2字符串相等宪塔,則返回零;若s1大于s2脉顿,則返回大于零的數(shù)蝌麸;否則,則返回小于零的數(shù)艾疟。
說明:strcmp()函數(shù)是根據(jù)ACSII碼的值來比較兩個(gè)字符串的来吩;strcmp()函數(shù)首先將s1字符串的第一個(gè)字符值減去s2第一個(gè)字符敢辩,若差值為零則繼續(xù)比較下去;若差值不為零弟疆,則返回差值戚长。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市怠苔,隨后出現(xiàn)的幾起案子同廉,更是在濱河造成了極大的恐慌,老刑警劉巖柑司,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迫肖,死亡現(xiàn)場離奇詭異,居然都是意外死亡攒驰,警方通過查閱死者的電腦和手機(jī)蟆湖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玻粪,“玉大人隅津,你說我怎么就攤上這事【⑹遥” “怎么了伦仍?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長很洋。 經(jīng)常有香客問我充蓝,道長,這世上最難降的妖魔是什么蹲缠? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任棺克,我火速辦了婚禮,結(jié)果婚禮上线定,老公的妹妹穿的比我還像新娘娜谊。我一直安慰自己,他們只是感情好斤讥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布纱皆。 她就那樣靜靜地躺著,像睡著了一般芭商。 火紅的嫁衣襯著肌膚如雪派草。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天铛楣,我揣著相機(jī)與錄音近迁,去河邊找鬼。 笑死簸州,一個(gè)胖子當(dāng)著我的面吹牛鉴竭,可吹牛的內(nèi)容都是我干的歧譬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼搏存,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼瑰步!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起璧眠,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤缩焦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后责静,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體袁滥,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年灾螃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呻拌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡睦焕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出靴拱,到底是詐尸還是另有隱情垃喊,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布袜炕,位于F島的核電站本谜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏偎窘。R本人自食惡果不足惜乌助,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望陌知。 院中可真熱鬧他托,春花似錦、人聲如沸仆葡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沿盅。三九已至把篓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間腰涧,已是汗流浹背韧掩。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留窖铡,地道東北人疗锐。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓坊谁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親窒悔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呜袁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 指針是C語言中廣泛使用的一種數(shù)據(jù)類型。 運(yùn)用指針編程是C語言最主要的風(fēng)格之一简珠。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu)阶界; ...
    朱森閱讀 3,444評(píng)論 3 44
  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學(xué)習(xí)記錄文檔,今天18年5月份再次想寫文章聋庵,發(fā)現(xiàn)簡書還為我保存起的...
    Jenaral閱讀 2,756評(píng)論 2 9
  • 版權(quán)聲明:本文為 gfson 原創(chuàng)文章膘融,轉(zhuǎn)載請(qǐng)注明出處。注:作者水平有限祭玉,文中如有不恰當(dāng)之處氧映,請(qǐng)予以指正,萬分感謝...
    gfson閱讀 2,983評(píng)論 0 6
  • 前言 將程序分成適當(dāng)?shù)淖园瑔卧情_發(fā)任意程序的基本方式脱货。在開發(fā)時(shí)岛都,應(yīng)該將其分成多個(gè)便于管理的部分,這樣帶來的好處...
    seraphzxz閱讀 1,387評(píng)論 0 48
  • 86.復(fù)合 Cases 共享相同代碼塊的多個(gè)switch 分支 分支可以合并, 寫在分支后用逗號(hào)分開振峻。如果任何模式...
    無灃閱讀 1,367評(píng)論 1 5