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

首頁 > 編程 > C > 正文

深入學習C語言中的函數指針和左右法則

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

通常的函數調用
    一個通常的函數調用的例子:

//自行包含頭文件void MyFun(int x); //此處的申明也可寫成:void MyFun( int );int main(int argc, char* argv[]){  MyFun(10); //這里是調用MyFun(10);函數   return 0;}void MyFun(int x) //這里定義一個MyFun函數{  printf(“%d/n”,x);}

    這個MyFun函數是一個無返回值的函數,它并不完成什么事情。這種調用函數的格式你應該是很熟悉的吧!看主函數中調用MyFun函數的書寫格式:
MyFun(10);
    我們一開始只是從功能上或者說從數學意義上理解MyFun這個函數,知道MyFun函數名代表的是一個功能(或是說一段代碼)。
    直到――
    學習到函數指針概念時。我才不得不在思考:函數名到底又是什么東西呢?
    (不要以為這是沒有什么意義的事噢!呵呵,繼續往下看你就知道了。)

函數指針變量的申明
    就象某一數據變量的內存地址可以存儲在相應的指針變量中一樣,函數的首地址也以存儲在某個函數指針變量里的。這樣,我就可以通過這個函數指針變量來調用所指向的函數了。
    在C系列語言中,任何一個變量,總是要先申明,之后才能使用的。那么,函數指針變量也應該要先申明吧?那又是如何來申明呢?以上面的例子為例,我來申明一個可以指向MyFun函數的函數指針變量FunP。下面就是申明FunP變量的方法:
void (*FunP)(int) ;   //也可寫成void (*FunP)(int x);
    你看,整個函數指針變量的申明格式如同函數MyFun的申明處一樣,只不過――我們把MyFun改成(*FunP)而已,這樣就有了一個能指向MyFun函數的指針FunP了。(當然,這個FunP指針變量也可以指向所有其它具有相同參數及返回值的函數了。)

通過函數指針變量調用函數
    有了FunP指針變量后,我們就可以對它賦值指向MyFun,然后通過FunP來調用MyFun函數了??次胰绾瓮ㄟ^FunP指針變量來調用MyFun函數的:

//自行包含頭文件void MyFun(int x); //這個申明也可寫成:void MyFun( int );void (*FunP)(int ); //也可申明成void(*FunP)(int x),但習慣上一般不這樣。int main(int argc, char* argv[]){  MyFun(10); //這是直接調用MyFun函數  FunP=&MyFun; //將MyFun函數的地址賦給FunP變量  (*FunP)(20); //這是通過函數指針變量FunP來調用MyFun函數的。}void MyFun(int x) //這里定義一個MyFun函數{  printf(“%d/n”,x);}

    請看黑體字部分的代碼及注釋。
    運行看看。嗯,不錯,程序運行得很好。
    哦,我的感覺是:MyFun與FunP的類型關系類似于int 與int *的關系。函數MyFun好像是一個如int的變量(或常量),而FunP則像一個如int *一樣的指針變量。

int i,*pi;pi=&i;  //與FunP=&MyFun比較。

    (你的感覺呢?)
    呵呵,其實不然――

調用函數的其它書寫格式
函數指針也可如下使用,來完成同樣的事情:

//自行包含頭文件void MyFun(int x); void (*FunP)(int ); //申明一個用以指向同樣參數,返回值函數的指針變量。int main(int argc, char* argv[]){  MyFun(10); //這里是調用MyFun(10);函數  FunP=MyFun; //將MyFun函數的地址賦給FunP變量  FunP(20); //這是通過函數指針變量來調用MyFun函數的。   return 0;}void MyFun(int x) //這里定義一個MyFun函數{  printf(“%d/n”,x);}

我改了黑體字部分(請自行與之前的代碼比較一下)。
運行試試,?。∫粯拥爻晒?。
咦?

FunP=MyFun;

可以這樣將MyFun值同賦值給FunP,難道MyFun與FunP是同一數據類型(即如同的int 與int的關系),而不是如同int 與int*的關系了?(有沒有一點點的糊涂了?)
看來與之前的代碼有點矛盾了,是吧!所以我說嘛!
請容許我暫不給你解釋,繼續看以下幾種情況(這些可都是可以正確運行的代碼喲?。?br />代碼之三:

int main(int argc, char* argv[]){  MyFun(10); //這里是調用MyFun(10);函數  FunP=&MyFun; //將MyFun函數的地址賦給FunP變量  FunP(20); //這是通過函數指針變量來調用MyFun函數的。   return 0;}

代碼之四:

int main(int argc, char* argv[]){  MyFun(10); //這里是調用MyFun(10);函數  FunP=MyFun; //將MyFun函數的地址賦給FunP變量  (*FunP)(20); //這是通過函數指針變量來調用MyFun函數的。   return 0;}

真的是可以這樣的噢!
(哇!真是要暈倒了?。?br />還有吶!看――

int main(int argc, char* argv[]){  (*MyFun)(10); //看,函數名MyFun也可以有這樣的調用格式   return 0;}

你也許第一次見到吧:函數名調用也可以是這樣寫的?。。ㄖ徊贿^我們平常沒有這樣書寫罷了。)
那么,這些又說明了什么呢?
呵呵!假使我是“福爾摩斯”,依據以往的知識和經驗來推理本篇的“新發現”,必定會由此分析并推斷出以下的結論:
1. 其實,MyFun的函數名與FunP函數指針都是一樣的,即都是函數指針。MyFun函數名是一個函數指針常量,而FunP是一個函數數指針變量,這是它們的關系。
2. 但函數名調用如果都得如(*MyFun)(10);這樣,那書寫與讀起來都是不方便和不習慣的。所以C語言的設計者們才會設計成又可允許MyFun(10);這種形式地調用(這樣方便多了并與數學中的函數形式一樣,不是嗎?)。
3. 為統一起見,FunP函數指針變量也可以FunP(10)的形式來調用。
4. 賦值時,即可FunP=&MyFun形式,也可FunP=MyFun。
上述代碼的寫法,隨便你愛怎么著!
請這樣理解吧!這可是有助于你對函數指針的應用嘍!
最后――
補充說明一點:在函數的申明處:

void MyFun(int );  //不能寫成void (*MyFun)(int )。void (*FunP)(int );  //不能寫成void FunP(int )。

(請看注釋)這一點是要注意的。

定義某一函數的指針類型:
就像自定義數據類型一樣,我們也可以先定義一個函數指針類型,然后再用這個類型來申明函數指針變量。
我先給你一個自定義數據類型的例子。

typedef int* PINT; //為int* 類型定義了一個PINT的別名int main(){ int x; PINT px=&x; //與int * px=&x;是等價的。PINT類型其實就是int * 類型 *px=10; //px就是int*類型的變量  return 0;}

根據注釋,應該不難看懂吧?。m然你可能很少這樣定義使用,但以后學習Win32編程時會經常見到的。)
下面我們來看一下函數指針類型的定義及使用:(請與上對照!)

//自行包含頭文件void MyFun(int x); //此處的申明也可寫成:void MyFun( int );typedef void (*FunType)(int ); //這樣只是定義一個函數指針類型FunType FunP; //然后用FunType類型來申明全局FunP變量int main(int argc, char* argv[]){//FunType FunP; //函數指針變量當然也是可以是局部的 ,那就請在這里申明了。   MyFun(10);   FunP=&MyFun;   (*FunP)(20);    return 0;}void MyFun(int x) {  printf(“%d/n”,x);}

看黑體部分:
首先,在void (*FunType)(int ); 前加了一個typedef 。這樣只是定義一個名為FunType函數指針類型,而不是一個FunType變量。
然后,FunType FunP;  這句就如PINT px;一樣地申明一個FunP變量。
其它相同。整個程序完成了相同的事。
這樣做法的好處是:
有了FunType類型后,我們就可以同樣地、很方便地用FunType類型來申明多個同類型的函數指針變量了。如下:

FunType FunP2;FunType FunP3;//……

函數指針作為某個函數的參數
既然函數指針變量是一個變量,當然也可以作為某個函數的參數來使用的。所以,你還應知道函數指針是如何作為某個函數的參數來傳遞使用的。
給你一個實例:
要求:我要設計一個CallMyFun函數,這個函數可以通過參數中的函數指針值不同來分別調用MyFun1、MyFun2、MyFun3這三個函數(注:這三個函數的定義格式應相同)。
實現:代碼如下:

//自行包含頭文件 void MyFun1(int x); void MyFun2(int x); void MyFun3(int x); typedef void (*FunType)(int ); //②. 定義一個函數指針類型FunType,與①函數類型一至void CallMyFun(FunType fp,int x);int main(int argc, char* argv[]){  CallMyFun(MyFun1,10); //⑤. 通過CallMyFun函數分別調用三個不同的函數  CallMyFun(MyFun2,20);   CallMyFun(MyFun3,30); }void CallMyFun(FunType fp,int x) //③. 參數fp的類型是FunType。{ fp(x);//④. 通過fp的指針執行傳遞進來的函數,注意fp所指的函數是有一個參數的}void MyFun1(int x) // ①. 這是個有一個參數的函數,以下兩個函數也相同{  printf(“函數MyFun1中輸出:%d/n”,x);}void MyFun2(int x) {  printf(“函數MyFun2中輸出:%d/n”,x);}void MyFun3(int x) {  printf(“函數MyFun3中輸出:%d/n”,x);}

輸出結果:略

分析:(看我寫的注釋。你可按我注釋的①②③④⑤順序自行分析。)
 
以上部分為轉載網友所述。原文地址為:http://blog.pfan.cn/whyhappy/6030.html
 
 
地址跳轉
void(*reset)(void)= (void(*)(void))0。
  void(*reset)(void)就是函數指針定義,(void(*)(void))0是強制類型轉換操作,將數值“0”強制轉換為函數指針地址“0”。
  通過調用reset()函數,程序就會跳轉到程序執行的“0”地址處重新執行。在一些其他高級單片機Bootloader中,如NBoot、UBoot、EBoot,經常通過這些Bootloader進行下載程序,然后通過函數指針跳轉到要執行程序的地址處。
1   void (*theUboot)(void);
    。。。。
    theUboot = (void (*)(void))(0x30700000);
    theUboot();
    。。。。。
2   (*(void (*)(void))(0x30700000))();
強制類型轉換,將一個絕對地址轉換為一個函數指針,并調用這個函數以跳轉到前面提到的絕對地址.
翻譯成匯編就是:
mov r0,0x30700000;
mov pc,r0
對于(*(void (*)(void))(0x30700000))();
可以這樣理解
首先(void( * )(void) )是一個強制類型轉換符,他將后面的0x30700000這個無符號整數強制轉化為一個函數指針,該函數指針所指向的函數入口參數為 void,返回值也是void 。 如果到這步你看懂了,那么設(void (*)(void))(0x30700000)為 fp; 那么上面的表達式就可以簡化為 (*fp)();   OK,這下就清楚了吧,我們將上面轉化好的函數指針進行引用(也就是調用函數指針指向的函數)。
 

右左法則

c語言有復雜的指針聲明,都是由各種聲明嵌套構成的。各大公司的筆試題里經常會出現理解復雜指針聲明,右左法則是一個著名又常用的方法。
The right-left rule : start reading the declaration from the innermost parentheses, go rigtht, and then go left. when you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.

右左規則:首先從最里面的括號看起,然后往右看,再往左看。每當遇到圓括號時,就應該掉轉閱讀方向。一旦解析完圓括號里面所有的東西,就跳出圓括號。重復這個過程直到整個聲明解析完畢。

原文作者對這里進行了修正:應該是從未定義的標識符開始閱讀,而不是從括號讀起,之所以是未定義的標識符,是因為一個聲明里可能有多個標識符,但未定義的標識符只會有一個

示例

  int (*func) (int *p); 


首先找到未定義標識符,就是func,它的外面有一對圓括號,而且左邊是一個*,這說明func是一個指針。然后跳出括號,看右邊,也是一個括號,這說明(*func)是一個函數,而func是一個指向這類函數的指針,也就是一個函數指針。這類函數具有int*類型的參數,返回值類型是int

  int (*func)(int *p, int (*f)(int *)); 


func被一對括號包含,且左邊有一個*號,說明func是一個指針,然后跳出這個圓括號,先看右邊,也是一個圓括號,說明func是一個函數指針。這類函數具有int *和 int (*)(int *)這樣的形參,返回值是int。對于int (*f)(int *)的形參,分析方法跟func是一致的

  int (*func[5])(int *p); 


func右邊是一個[]運算符,說明func是一個具有5個元素的數組,func的左邊有一個*,說明func的元素是指針,要注意這里的*不是修飾func的,而是修飾func[5]的,原因是[]運算符的優先級比*高,func先跟[]結合,因此*修飾的是func[5].跳出這個括號,看右邊,也是一對圓括號,說明func數組的元素是函數類型的指針,它指向的函數具有int*類型的形參,返回值類型是int

  int (*(*func)[5])(int *p); 


func被一對圓括號包圍,左邊又有一個*,那么func是一個指針,跳出括號,右邊是一個[]運算符號,說明func是一個指向數組的指針,現在往左看,左邊有一個*號,說明這個數組的元素是指針,再跳出括號,向右看,右邊又有一個括號,說明這個數組的元素是指向函數的指針??偨Y一下就是:func是一個指向數組的指針,這個數組的元素是函數指針,這些指針具有int *形參,返回值為int類型的函數

  int (*(*func)(int *p))[5]; 


func是一個函數指針,這類函數具有int *類型的形參,返回值是指向數組的指針,所指向的是具有5個int類型元素的數組。

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

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
免费91在线视频| 欧美成人午夜剧场免费观看| 97婷婷大伊香蕉精品视频| 亚洲成avwww人| 国产在线观看一区二区三区| 欧美三级免费观看| 国产xxx69麻豆国语对白| 欧美日韩亚洲天堂| 欧美极品xxxx| 亚洲一区二区三区乱码aⅴ| 久久久久久久成人| 国产日韩在线亚洲字幕中文| 日本精品免费观看| 久久不射热爱视频精品| 色妞欧美日韩在线| 精品激情国产视频| 日韩最新av在线| 一本色道久久88综合亚洲精品ⅰ| 亚洲最大福利视频网站| 欧美激情视频免费观看| 欧美国产日韩一区二区在线观看| 欧美视频在线观看免费| 日韩国产在线播放| 国产精品精品国产| 日韩免费av片在线观看| 日韩亚洲一区二区| 成年无码av片在线| 欧美精品电影免费在线观看| 久久久在线视频| 欧美黄色www| 狠狠躁18三区二区一区| 伊人久久精品视频| 亚洲欧美国产va在线影院| 日韩电影免费观看在线| 国外成人在线视频| 国产成人+综合亚洲+天堂| 欧美午夜丰满在线18影院| 亚洲一区中文字幕在线观看| 亚洲日本欧美日韩高观看| 欧美成人免费小视频| 久久香蕉国产线看观看av| 欧美国产高跟鞋裸体秀xxxhd| 精品久久久久久久久中文字幕| 亚洲色图五月天| 久久久久久久97| 成人黄色免费在线观看| 亚洲影视九九影院在线观看| 一区二区三区高清国产| 亚洲日韩中文字幕在线播放| 精品呦交小u女在线| 91视频-88av| 国产精品视频在线播放| 91产国在线观看动作片喷水| 亚洲精品美女在线观看| 欧美视频免费在线观看| 亚洲激情视频在线观看| 久久精品99国产精品酒店日本| 亚洲欧美www| 亚洲国产精品一区二区三区| 日韩精品在线看| 北条麻妃一区二区在线观看| 欧美日韩aaaa| 久久亚洲精品小早川怜子66| 最新日韩中文字幕| 亚洲网在线观看| 中文字幕国产精品久久| 亚洲性69xxxbbb| 91亚洲永久免费精品| 日韩中文字幕在线精品| 中文字幕亚洲欧美| 国产精品一区二区三区在线播放| 亚洲国产精品高清久久久| 亚洲自拍偷拍区| 亚洲精品www久久久| 国内精品400部情侣激情| 亚洲国产另类 国产精品国产免费| 日韩在线观看你懂的| 欧美激情第99页| 在线观看欧美www| 欧美激情啊啊啊| 欧美成人性色生活仑片| 国产精品91久久久| 久久国产精品电影| 一区二区成人精品| 亚洲欧美一区二区精品久久久| 成人国产精品一区二区| 日韩视频免费观看| 大伊人狠狠躁夜夜躁av一区| 亚洲精品456在线播放狼人| 欧洲成人在线视频| 欧美有码在线观看| 亚洲第一综合天堂另类专| 日本精品性网站在线观看| 丰满岳妇乱一区二区三区| 91久久精品国产91久久| 亚洲欧美激情四射在线日| 日本最新高清不卡中文字幕| 欧美在线一级va免费观看| 亚洲欧美制服中文字幕| 国产91精品久久久久久久| 97碰碰碰免费色视频| 欧美福利小视频| 久久久久久久香蕉网| 国产成人精品综合久久久| y97精品国产97久久久久久| 欧美日本精品在线| 亚洲精品国产精品乱码不99按摩| 亚洲视频在线播放| 国产原创欧美精品| 九九热最新视频//这里只有精品| 日韩a**中文字幕| 日韩成人中文电影| 亚洲人av在线影院| 欧美午夜宅男影院在线观看| 久久精品久久久久| 久久久久免费精品国产| 国产精品久久一区| 久久久久99精品久久久久| 一个色综合导航| 国产在线98福利播放视频| 国产不卡av在线| 国产精品久久久久7777婷婷| 亚洲成人999| 伊人伊人伊人久久| 日产日韩在线亚洲欧美| 91亚洲va在线va天堂va国| 欧美日韩精品在线| 亚洲精品电影在线观看| 精品呦交小u女在线| 97色伦亚洲国产| 中国china体内裑精亚洲片| 亚洲a在线播放| 国产91色在线| 中文字幕精品视频| 亚洲美女中文字幕| 美女撒尿一区二区三区| 高清一区二区三区四区五区| xxxx性欧美| 国产视频一区在线| 日韩免费在线播放| 91久久国产综合久久91精品网站| 日韩精品极品毛片系列视频| 亚洲mm色国产网站| 亚洲资源在线看| 亚洲国产日韩欧美综合久久| 98精品在线视频| 亚洲天堂av高清| 欧美日韩性生活视频| 国产精品扒开腿做爽爽爽视频| 欧美日韩成人在线视频| 国产日产久久高清欧美一区| 国产一区二区久久精品| 激情懂色av一区av二区av| 亚洲人成网站在线播| 高潮白浆女日韩av免费看| 97国产在线视频| 久久久久亚洲精品成人网小说| 国产成人黄色av| 欧美日韩中国免费专区在线看| 久久精品电影网站| 国产精品亚洲网站| www日韩中文字幕在线看| 成人黄色免费在线观看| 精品久久久久久久久久久久|