欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語言中的函數(shù)指針基礎(chǔ)學(xué)習(xí)教程

 更新時(shí)間:2016年04月20日 14:47:56   作者:Dennis Kubes  
這篇文章主要介紹了C語言中的函數(shù)指針基礎(chǔ)學(xué)習(xí)教程,包括函數(shù)指針作為參數(shù)來傳遞等重要知識(shí),需要的朋友可以參考下

顧名思義,函數(shù)指針就是函數(shù)的指針。它是一個(gè)指針,指向一個(gè)函數(shù)??蠢樱?/p>

A)

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

B)

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

C)

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

看看上面三個(gè)表達(dá)式分別是什么意思?

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

int (*)[10] p;

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

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

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


函數(shù)指針和一個(gè)簡單的函數(shù)

我們從一個(gè)非常簡單的”Hello World“函數(shù)入手,來見識(shí)一下怎樣創(chuàng)建一個(gè)函數(shù)指針。

#include <stdio.h>
 
// 函數(shù)原型
void sayHello();
 
//函數(shù)實(shí)現(xiàn)
void sayHello(){
  printf("hello world\n");
}
 
// main函數(shù)調(diào)用
int main() {
  sayHello();
}

我們定義了一個(gè)名為sayHello的函數(shù),它沒有返回值也不接受任何參數(shù)。當(dāng)我們在main函數(shù)中調(diào)用它的時(shí)候,它向屏幕輸出出”hello world“。非常簡單。接下來,我們改寫一下main函數(shù),之前直接調(diào)用的sayHello函數(shù),現(xiàn)在改用函數(shù)指針來調(diào)用它。

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

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

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

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

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

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

帶參數(shù)的函數(shù)指針

好了,這一次我們來創(chuàng)建一個(gè)新的函數(shù)指針吧。它指向的函數(shù)仍然不返回任何值,但有了參數(shù)。

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

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

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


帶參數(shù)且有返回值的函數(shù)指針

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

#include <stdio.h>
 
// 函數(shù)原型
int subtract(int x, int y);
 
// 函數(shù)實(shí)現(xiàn)
int subtract(int x, int y) {
  return x - y;
}
 
// main函數(shù)調(diào)用
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函數(shù)非常相似,只是subtract函數(shù)返回了一個(gè)整數(shù)而已,特征標(biāo)也理所當(dāng)然的不一樣了。

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

把函數(shù)指針作為參數(shù)來傳遞

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

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

我們來一步一步分析。

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

函數(shù)名和地址

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

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

這是一個(gè)簡單的例子。運(yùn)行這段代碼,你會(huì)看到每個(gè)函數(shù)指針都會(huì)執(zhí)行,只是會(huì)收到一些關(guān)于字符轉(zhuǎn)換的警告。但是,這些函數(shù)指針都能正常工作。

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

相關(guān)文章

  • c++中struct和class的區(qū)別小結(jié)

    c++中struct和class的區(qū)別小結(jié)

    在C++中,class和struct都是用于定義自定義數(shù)據(jù)類型的關(guān)鍵字,本文主要介紹了c++中struct和class的區(qū)別小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • C++類的靜態(tài)成員變量與靜態(tài)成員函數(shù)詳解

    C++類的靜態(tài)成員變量與靜態(tài)成員函數(shù)詳解

    下面小編就為大家?guī)硪黄狢++類的靜態(tài)成員變量與靜態(tài)成員函數(shù)的文章。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2021-11-11
  • 對(duì)稱矩陣的壓縮儲(chǔ)存講解

    對(duì)稱矩陣的壓縮儲(chǔ)存講解

    今天小編就為大家分享一篇關(guān)于對(duì)稱矩陣的壓縮儲(chǔ)存講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • C++用winapi?socket實(shí)現(xiàn)局域網(wǎng)語音通話功能

    C++用winapi?socket實(shí)現(xiàn)局域網(wǎng)語音通話功能

    這篇文章主要介紹了socket實(shí)現(xiàn)局域網(wǎng)語音通話?c++?winapi,功能介紹支持錄音設(shè)備查找以及播放設(shè)備查找,支持局域網(wǎng)語音通話,通話包含語音來電提醒和掛斷電話的提示信息,還能實(shí)時(shí)的獲取在線用戶的數(shù)量以及對(duì)應(yīng)的id,需要的的朋友一起看看
    2022-06-06
  • 關(guān)于"引用"的幾點(diǎn)說明介紹

    關(guān)于"引用"的幾點(diǎn)說明介紹

    引用聲明完畢后,相當(dāng)于目標(biāo)變量名有兩個(gè)名稱,即該目標(biāo)原名稱和引用名,且不能再把該引用名作為其他變量名的別名
    2013-09-09
  • 關(guān)于C語言多線程pthread庫的相關(guān)函數(shù)說明

    關(guān)于C語言多線程pthread庫的相關(guān)函數(shù)說明

    下面小編就為大家?guī)硪黄P(guān)于C語言多線程pthread庫的相關(guān)函數(shù)說明。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • C語言簡單實(shí)現(xiàn)三子棋游戲

    C語言簡單實(shí)現(xiàn)三子棋游戲

    這篇文章主要為大家詳細(xì)介紹了C語言簡單實(shí)現(xiàn)三子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Qt+OpenCV利用幀差法實(shí)現(xiàn)車輛識(shí)別

    Qt+OpenCV利用幀差法實(shí)現(xiàn)車輛識(shí)別

    所謂幀差法也就是對(duì)連續(xù)圖像幀做差分運(yùn)算,其結(jié)果與定義好的閾值比較,若大于閾值則為運(yùn)動(dòng)目標(biāo)值為1,否則值為0?。本文將利用幀差法實(shí)現(xiàn)車輛識(shí)別,感興趣的可以了解一下
    2022-08-08
  • C語言求連續(xù)最大子數(shù)組和的方法

    C語言求連續(xù)最大子數(shù)組和的方法

    這篇文章主要介紹了C語言求連續(xù)最大子數(shù)組和的方法,包含了數(shù)組的常見操作及相關(guān)技巧,需要的朋友可以參考下
    2014-09-09
  • C++ list的實(shí)例詳解

    C++ list的實(shí)例詳解

    這篇文章主要介紹了 C++ list的實(shí)例詳解的相關(guān)資料,希望通過本文大家能夠理解掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-09-09

最新評(píng)論