亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

C語言中的函數指針基礎學習教程

2020-01-26 14:38:24
字體:
來源:轉載
供稿:網友

顧名思義,函數指針就是函數的指針。它是一個指針,指向一個函數。看例子:

A)

char * (*fun1)(char * p1,char * p2);

B)

char * *fun2(char * p1,char * p2);

C)

char * fun3(char * p1,char * p2);

看看上面三個表達式分別是什么意思?

C)這很容易,fun3是函數名,p1,p2是參數,其類型為char *型,函數的返回值為char *類型。
B) 也很簡單,與C)表達式相比,唯一不同的就是函數的返回值類型為char**,是個二級指針。
A) fun1是函數名嗎?回憶一下前面講解數組指針時的情形。我們說數組指針這么定義或許更清晰:

int (*)[10] p;

再看看A)表達式與這里何其相似!明白了吧。這里fun1不是什么函數名,而是一個指針變量,它指向一個函數。這個函數有兩個指針類型的參數,函數的返回值也是一個指針。同樣,我們把這個表達式改寫一下:

char * (*)(char * p1,char * p2) fun1;

這樣子是不是好看一些呢?只可惜編譯器不這么想。^_^。


函數指針和一個簡單的函數

我們從一個非常簡單的”Hello World“函數入手,來見識一下怎樣創建一個函數指針。

#include <stdio.h> // 函數原型void sayHello(); //函數實現void sayHello(){  printf("hello world/n");} // main函數調用int main() {  sayHello();}

我們定義了一個名為sayHello的函數,它沒有返回值也不接受任何參數。當我們在main函數中調用它的時候,它向屏幕輸出出”hello world“。非常簡單。接下來,我們改寫一下main函數,之前直接調用的sayHello函數,現在改用函數指針來調用它。

int main() {  void (*sayHelloPtr)() = sayHello;  (*sayHelloPtr)();}

第二行void (*sayHelloPtr)()的語法看起來有些奇怪,我們來一步一步分析。

這里,關鍵字void的作用是說我們創建了一個函數指針,并讓它指向了一個返回void(也就是沒有返回值)的函數。
就像其他任何指針都必須有一個名稱一樣,這里sayHelloPtr被當作這個函數指針的名稱。
我們用*符號來表示這是一個指針,這跟聲明一個指向整數或者字符的指針沒有任何區別。
*sayHelloPtr兩端的括號是必須的,否則,上述聲明變成void *sayHelloPtr(),*會優先跟void結合,變成了一個返回指向void的指針的普通函數的聲明。因此,函數指針聲明的時候不要忘記加上括號,這非常關鍵。
參數列表緊跟在指針名之后,這個例子中由于沒有參數,所以是一對空括號()。
將上述要點結合起來,void (*syaHelloPtr)()的意義就非常清楚了,這是一個函數指針,它指向一個不接收參數且沒有返回值的函數。
在上面的第二行代碼,即void (*sayHelloPtr)() = sayHello;,我們將sayHello這個函數名賦給了我們新建的函數指針。關于函數名的更多細節我們會在下文中討論,現在暫時可以將其看作一個標簽,它代表函數的地址,并且可以賦值給函數指針。這就跟語句int *x = &myint;中我們把myint的地址賦給一個指向整數的指針一樣。只是當我們考慮函數的時候,我們不需要加上一個取地址符&。簡而言之,函數名就是它的地址。接著看第三行,我們用代碼'(*sayHelloPtr)();?‘解引用并調用了函數指針。

在第二行被聲明之后,sayHelloPtr作為函數指針的名稱,跟其他任何指針沒有差別,能夠儲值和賦值。
我們對sayHelloPtr解引用的方式也與其他任何指針一樣,即在指針之前使用解引用符*,也就是代碼中的*sayHelloPtr。
同樣的,我們需要在其兩端加上括號,即(*sayHelloPtr),否則它就不被當做一個函數指針。因此,記得聲明和解引用的時候都要在兩端加上括號。
括號操作符用于C語言中的函數調用,如果有參數參與,就將其放入括號中。這對于函數指針也是相似的,即代碼中的(*sayHelloPtr)()。
這個函數沒有返回值,也就沒有必要將它賦值給任何變量。單獨來說,這個調用跟sayHello()沒什么兩樣。
接下來,我們再對函數稍加修改。你會看到函數指針奇怪的語法,以及用調用普通函數的方法來調用賦值后函數指針的現象。

int main() {void (*sayHelloPtr)() = sayHello;sayHelloPtr();}

跟之前一樣,我們將sayHello函數賦給函數指針。但是這一次,我們用調用普通函數的方法調用了它。稍后討論函數名的時候我會解釋這一現象,現在只需要知道(*syaHelloPtr)()和syaHelloPtr()是相同的即可。

帶參數的函數指針

好了,這一次我們來創建一個新的函數指針吧。它指向的函數仍然不返回任何值,但有了參數。

#include <stdio.h> //函數原型void subtractAndPrint(int x, int y); //函數實現void subtractAndPrint(int x, int y) {  int z = x - y;  printf("Simon says, the answer is: %d/n", z);} //main函數調用int main() {  void (*sapPtr)(int, int) = subtractAndPrint;  (*sapPtr)(10, 2);  sapPtr(10, 2);}

跟之前一樣,代碼包括函數原型,函數實現和在main函數中通過函數指針執行的語句。原型和實現中的特征標變了,之前的sayHello函數不接受任何參數,而這次的函數subtractAndPrint接受兩個int作為參數。它將兩個參數做一次減法,然后輸出到屏幕上。

在第14行,我們通過'(*sapPtr)(int, int)'創建了sapPtr這個函數指針,與之前的區別僅僅是用(int, int)代替了原來的空括號。而這與新函數的特征標相符。
在第15行,解引用和執行函數的方式與之前完全相同,只是在括號中加入了兩個參數,變成了(10, 2)。
在第16行,我們用調用普通函數的方法調用了函數指針。


帶參數且有返回值的函數指針

這一次,我們把subtractAndPrint函數改成一個名為subtract的函數,讓它把原本輸出到屏幕上的結果作為返回值。

#include <stdio.h> // 函數原型int subtract(int x, int y); // 函數實現int subtract(int x, int y) {  return x - y;} // main函數調用int main() { int (*subtractPtr)(int, int) = subtract;  int y = (*subtractPtr)(10, 2); printf("Subtract gives: %d/n", y);  int z = subtractPtr(10, 2); printf("Subtract gives: %d/n", z);}

這與subtractAndPrint函數非常相似,只是subtract函數返回了一個整數而已,特征標也理所當然的不一樣了。

在第13行,我們通過int (*subtractPtr)(int, int)創建了subtractPtr這個函數指針。與上一個例子的區別只是把void換成了int來表示返回值。而這與subtract函數的特征標相符。
在在第15行,解引用和執行這個函數指針,除了將返回值賦值給了y以外,與調用subtractAndPrint沒有任何區別。
在第16行,我們向屏幕輸出了返回值。
18到19行,我們用調用普通函數的方法調用了函數指針,并且輸出了結果。
這跟之前沒什么兩樣,我們只是加上了返回值而已。接下來我們看看另一個稍微復雜點兒的例子――把函數指針作為參數傳遞給另一個函數。

把函數指針作為參數來傳遞

我們已經了解過了函數指針聲明和執行的各種情況,不論它是否帶參數,或者是否有返回值。接下來我們利用一個函數指針來根據不同的輸入執行不同的函數。

#include <stdio.h> // 函數原型int add(int x, int y);int subtract(int x, int y);int domath(int (*mathop)(int, int), int x, int y); // 加法 x+ yint add(int x, init y) {  return x + y;} // 減法 x - yint subtract(int x, int y) {  return x - y;} // 根據輸入執行函數指針int domath(int (*mathop)(int, int), int x, int y) {  return (*mathop)(x, y);} // main函數調用int main() { // 用加法調用domathint a = domath(add, 10, 2);printf("Add gives: %d/n", a); // 用減法調用domathint b = domath(subtract, 10, 2);printf("Subtract gives: %d/n", b);}

我們來一步一步分析。

我們有兩個特征標相同的函數,add和subtract,它們都返回一個整數并接受兩個整數作為參數。
在第六行,我們定義了函數int domath(int (*mathop)(int, int), int x, int y)。它第一個參數int (*mathop)(int, int)是一個函數指針,指向返回一個整數并接受兩個整數作為參數的函數。這就是我們之前見過的語法,沒有任何不同。它的后兩個整數參數則作為簡單的輸入。因此,這是一個接受一個函數指針和兩個整數作為參數的函數。
19到21行,domath函數將自己的后兩個整數參數傳遞給函數指針并調用它。當然,也可以像這么調用。mathop(x, y);
27到31行出現了我們沒見過的代碼。我們用函數名作為參數調用了domath函數。就像我之前說過的,函數名是函數的地址,而且能代替函數指針使用。
main函數調用了兩次domath函數,一次用了add,一次用了subtract,并輸出了這兩次結果。

函數名和地址

既然有約在先,那我們就討論一下函數名和地址作為結尾吧。一個函數名(或稱標簽),被轉換成了一個指針本身。這表明在函數指針被要求當作輸入的地方,就能夠使用函數名。這也導致了一些看起來很糟糕的代碼卻能夠正確的運行。瞧瞧下面這個例子。

#include <stdio.h> // 函數原型void add(char *name, int x, int y); // 加法 x + yvoid add(char *name, int x, int y) {  printf("%s gives: %d/n", name, x + y);} // main函數調用int main() {   // 一些糟糕的函數指針賦值  void (*add1Ptr)(char*, int, int) = add;  void (*add2Ptr)(char*, int, int) = *add;  void (*add3Ptr)(char*, int, int) = &add;  void (*add4Ptr)(char*, int, int) = **add;  void (*add5Ptr)(char*, int, int) = ***add;   // 仍然能夠正常運行  (*add1Ptr)("add1Ptr", 10, 2);  (*add2Ptr)("add2Ptr", 10, 2);  (*add3Ptr)("add3Ptr", 10, 2);  (*add4Ptr)("add4Ptr", 10, 2);  (*add5Ptr)("add5Ptr", 10, 2);   // 當然,這也能運行  add1Ptr("add1PtrFunc", 10, 2);  add2Ptr("add2PtrFunc", 10, 2);  add3Ptr("add3PtrFunc", 10, 2);  add4Ptr("add4PtrFunc", 10, 2);  add5Ptr("add5PtrFunc", 10, 2);}

這是一個簡單的例子。運行這段代碼,你會看到每個函數指針都會執行,只是會收到一些關于字符轉換的警告。但是,這些函數指針都能正常工作。

在第15行,add作為函數名,返回這個函數的地址,它被隱式的轉換為一個函數指針。我之前提到過,在函數指針被要求當作輸入的地方,就能夠使用函數名。
在第16行,解引用符作用于add之前,即*add,在返回在這個地址的函數。之后跟函數名一樣,它被隱式的轉換為一個函數指針。
在第17行,取地址符作用于add之前,即&add,返回這個函數的地址,之后又得到一個函數指針。
18到19行,add不斷地解引用自身,不斷返回函數名,并被轉換為函數指針。到最后,它們的結果都和函數名沒有區別。
顯然,這段代碼不是優秀的實例代碼。我們從中收獲到了如下知識:其一,函數名會被隱式的轉換為函數指針,就像作為參數傳遞的時候,數組名被隱式的轉換為指針一樣。在函數指針被要求當作輸入的任何地方,都能夠使用函數名。其二,解引用符*和取地址符&用在函數名之前基本上都是多余的。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91久久精品国产91久久| 日韩美女主播视频| 日韩一区二区av| 九九热精品视频| 国产亚洲日本欧美韩国| 欧美国产日产韩国视频| 亚洲国产精品久久91精品| 久久久久久久久中文字幕| 国产精品电影久久久久电影网| 日韩av在线一区二区| 97超级碰碰碰久久久| 亚洲第一综合天堂另类专| 亚洲毛片一区二区| 亚洲人高潮女人毛茸茸| 亚洲欧美中文日韩v在线观看| 国产精品成人一区二区三区吃奶| 丝袜美腿亚洲一区二区| 久久免费高清视频| 日韩av男人的天堂| 国产精品1区2区在线观看| 亚洲精品v欧美精品v日韩精品| 欧美丝袜美女中出在线| 国产一区二区久久精品| 青草热久免费精品视频| 欧美精品videos| 中文字幕亚洲激情| 九九热99久久久国产盗摄| 欧美插天视频在线播放| 亚洲男子天堂网| 国内成人精品一区| 久久男人的天堂| 精品久久在线播放| 国产日韩欧美中文在线播放| 91天堂在线视频| 91视频九色网站| 亚洲欧美在线第一页| 日本亚洲欧洲色| 国产精品久久久久久亚洲调教| 久久久国产一区二区| 亚洲电影免费观看高清| 538国产精品视频一区二区| 亚洲综合视频1区| 日本精品va在线观看| 国产亚洲美女精品久久久| 91av在线播放| 国产精品久久久久久av下载红粉| 最近2019年手机中文字幕| 亚洲成avwww人| 黑人巨大精品欧美一区二区| 亚洲97在线观看| 美女国内精品自产拍在线播放| 国产一区二区三区在线免费观看| 91成人国产在线观看| 国产成人av在线| 国产精品羞羞答答| 91精品国产网站| 亚洲欧美一区二区三区四区| 欧美激情一区二区三区高清视频| 日韩欧美亚洲国产一区| 成人免费观看49www在线观看| 91精品视频免费观看| 2019精品视频| 日韩中文字幕网| 久久亚洲精品中文字幕冲田杏梨| 日韩电影中文字幕一区| 中文字幕9999| 中文字幕亚洲天堂| 91精品国产高清久久久久久91| 久久久亚洲福利精品午夜| 欧美激情国产高清| 国产精品99久久久久久www| 久久久久久久久久久国产| 精品日本高清在线播放| 国产成人精品综合久久久| 亚洲精品久久久久中文字幕欢迎你| 亚洲国产成人在线播放| 日本高清不卡在线| 日韩网站免费观看| 久久综合伊人77777| 亚洲第一天堂av| 成人性教育视频在线观看| 久久免费视频观看| 91久久综合亚洲鲁鲁五月天| 亚洲精品国产综合区久久久久久久| 日本欧美爱爱爱| 国产精品美女免费看| 欧美激情免费观看| 狠狠躁夜夜躁久久躁别揉| 亚洲2020天天堂在线观看| www.欧美精品| 精品一区二区电影| www欧美日韩| 在线播放精品一区二区三区| 国产98色在线| 91中文精品字幕在线视频| 久久久99久久精品女同性| 综合久久五月天| 亚洲精品国产电影| 三级精品视频久久久久| 一本色道久久88综合日韩精品| 亚洲伊人第一页| 亚洲美女av在线播放| 成人女保姆的销魂服务| 欧美黄色片在线观看| 亚洲国产成人精品一区二区| 亚洲国产精品va| 国内偷自视频区视频综合| 亚洲精品自在久久| 久久大大胆人体| 欧美精品久久久久| 亚洲免费成人av电影| 亚洲欧洲黄色网| 日韩在线精品一区| 国产综合福利在线| 欧美日韩精品国产| 成人免费黄色网| 国产精品欧美一区二区三区奶水| 亚洲欧美成人在线| 亚洲字幕一区二区| 2019中文字幕在线| 欧美影院久久久| 国产ts一区二区| 久久精品人人做人人爽| 成人免费视频网址| 亚洲国产精品99久久| 欧美色图在线视频| 欧美三级免费观看| 国产婷婷成人久久av免费高清| 免费97视频在线精品国自产拍| 91在线播放国产| 日韩亚洲欧美中文高清在线| 国产福利精品视频| 久久伊人免费视频| 成人精品网站在线观看| 亚洲人成电影网站色www| 久久精品一本久久99精品| 久久视频免费在线播放| 亚洲网站在线播放| 韩国三级日本三级少妇99| 欧美午夜www高清视频| 少妇高潮 亚洲精品| 亚洲欧美日韩图片| 国产精品欧美日韩一区二区| 色偷偷综合社区| 久久激情视频久久| 国产精品人人做人人爽| 国产aaa精品| 国产精品视频资源| 亚洲女人天堂成人av在线| 欧美精品中文字幕一区| 日韩av在线网址| 国产亚洲欧洲黄色| 91久久精品国产91久久性色| 欧美性猛交xxxx乱大交| 国产欧美va欧美va香蕉在线| 日韩在线视频网站| 91高清视频在线免费观看| 欧美性开放视频| 亚洲人午夜精品| 欧美性猛交xxxx免费看| 狠狠色狠狠色综合日日五| 欧美激情视频网| 欧美国产极速在线| 欧美人与物videos|