<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          函數指針與指針函數之間的愛恨情仇

          共 3346字,需瀏覽 7分鐘

           ·

          2020-09-13 10:43


          來源:C語言與CPP編程

          作者:LeeWay


          關于指針,前面文章C語言指針詳解有過介紹,這里主要討論函數指針和指針函數。

          1 什么是指針?

          定義:指針是程序數據在內存中的地址,而指針變量是用來保存這些地址的變量;

          上面一個 4GB 的內存可以存放 2^32 字節(jié)的數據。左側連續(xù)的十六進制編號就是內存地址,每個內存地址對應一個字節(jié)的內存空間。而指針變量保存的就是這個編號,也即內存地址。

          指針的聲明:

          指針其實就是一個變量,指針的聲明方式與一般的變量聲明類似,如下:

          int?*p;?????????//?聲明一個?int?類型的指針?p,該指針指向一個int類型的對象
          char?*p?????????//?聲明一個?char?類型的指針?p,該指針指向一個int類型的對象
          int?*arr[10]????//?聲明一個指針數組,該數組有10個元素,其中每個元素都是一個指向?int?類型對象的指針
          int?(*arr)[10]??//?聲明一個數組指針,該指針指向一個?int?類型的一維數組
          int?**p;????????//?聲明一個指針?p?,該指針指向一個?int?類型的指針

          聲明一個指針變量并不會自動分配任何內存。在對指針進行間接訪問之前,指針必須進行初始化:或是使他指向現有的內存,或者給他動態(tài)分配內存,否則我們并不知道指針指向哪兒,這個問題需要特別關注。

          2 什么是函數指針?

          函數指針定義:函數指針是指向函數的指針變量。因此“函數指針”本身首先應是指針變量,只不過該指針變量指向函數。這正如用指針變量可指向整型變量、字符型、數組一樣,這里是指向函數。

          其通用表達式為:類型說明符 (*函數名) (參數)

          int?(*fun)(int?x)??//函數指針的定義
          int?(*fun)(int?x,int?y)?//函數指針的定義

          函數指針在PC軟件開發(fā)中使用較少,在嵌入式行業(yè)使用較多,但是無論是PC軟件還是嵌入式軟件,理解函數指針的定義和使用,對于理解程序設計都是很有好處的。

          函數指針的賦值

          函數指針和其他指針一樣定義之后使用之前也是需要初始化。

          函數指針有兩個用途:調用函數做函數的參數

          int?(*fun)(int?x,int?y)?//函數指針的定義
          fun?=?&Function??????????//函數指針的賦值方式1
          fun?=?Function???????????//函數指針的賦值方式2
          x?=?(*fun)()?????????????//函數指針的調用方式1
          x?=?fun()????????????????//函數指針的調用方式2,錯誤

          函數賦值的時候取地址運算符&不是必需的,因為一個函數標識符就表示了它的地址,并且賦值的時候函數不需要帶圓括號;

          如果是函數調用,還必須包含一個圓括號括起來的參數表。

          函數指針的用法

          我們使用指針的時候,需要通過鑰匙 * 來取其指向的內存里面的值,函數指針使用也如此。通過用(*pf)取出存在這個地址上的函數,然后調用它。

          char*??fun(char*?p1,char*?p2)
          {
          ??int?i?=?0;
          ??i?=?strcmp(p1,p2);
          ??if(0?==?i)
          ??{
          ????return?p1;
          ??}
          ??else
          ??{
          ?????return?p2;
          ??}
          }
          int?main()
          {
          ??char?*?(*pf)(char*?p1,char*?p2);
          ??pf?=?&fun;
          ??(*pf)("aa","bb");
          ??return?0;
          }

          這里需要注意到是,在Visual C++6.0里,給函數指針賦值時,可以用&fun或直接用函數名fun。這是因為函數名被編譯之后其實就是一個地址,所以這里兩種用法沒有本質的差別。

          用法延申

          當我們不滿足于函數指針上面如此簡單的用法時,這時候需要一個高級用法來擴展我們對于函數指針的認知邊界。

          感興趣的同學可以看看下面這個用法,并嘗試理解該表達式是如何使用的函數指針。

          (*?(void(*)())?0)();?//出自《C?Trap?and?Pitfalls》這本經典的書

          答案如下:? ?``

          • 第一步:通過void(*) (),可以明白這是一個函數指針類型。這個函數沒有參數,沒有返回值。
          • 第二步:通過(void(*) ())0,可以明白這是將0強制轉換為函數指針類型,0是一個地址,也就是說一個函數存在首地址為0的一段區(qū)域內。
          • 第三步:通過(*(void(*) ())0),可以明白這是取0地址開始的一段內存里面的內容。
          • 第四步:最終理解(*(void(*) ())0)(),這是函數調用。

          讓程序跳轉到絕對地址為0x0113F90C

          方法一:

          • 0x0113F90C地址強制轉換為函數指針類型,即: (void (*)())0x0113F90C
          • 然后調用:((void (*)())0x0113F90C)()

          方法二:

          typedef?(void?(*)())??VoidFuncPtr;
          ((VoidFuncPtr)0x0113F90C)();

          面試題:指出程序的錯誤

          #include

          void?main(void)
          {
          ???int?max(x,y);
          ???int?*p=max;
          ???int?a,b,c,d;
          ???scanf("%d?%d?%d",a,b,c);
          ???d=p(p(a,b),c);
          ???printf("d:%d\n",d);
          ???return;
          }
          int?max(int?x,int?y)
          {
          ???return(x > y ?x:y);
          }

          答案

          • int max(x ,y);函數聲明錯誤,改為:int max(int x,int y);

          解析:max函數聲明只是寫出了函數的形參的名稱,這對參數的類型來說是毫無意義的,編譯器會把xy當做數據類型來看,編譯時會出錯,max的調用肯定也會出錯。

          • int *p=max;指針定義錯誤,改為:int (*p)(int ,int)=max;

          解析:函數的指針是不能直接賦值給int型指針.

          • scanf("%d %d %d",a,b,c);庫函數使用錯誤,改為scanf("%d %d%d",&a,&b,&c);

          解析:庫函數使用錯誤,第二部分應該是接收數據的地址,這里卻寫成了變量。

          • d=p(p(a,b),c);函數指針調用函數錯誤,改為d=(*p)((*p)(a,b),c);`

          解析:用函數指針調用函數的格式如下:(【*】【函數指針名稱】)(【參數列表】);不能直接用函數指針加上參數就直接調用

          3 什么是指針函數?

          指針函數定義:指針函數的落腳點是一個函數,這個函數的返回值是一個指針,與普通函數int function(int,int)類似,只是返回的數據類型不一樣而已。

          _type_?*function(int,?int)?//返回的是指針地址
          int?function(int,int)?????//返回的是int型數據。
          int??*fun(int?x)????????//指針函數的定義
          int?*?fun(int?x,int?y)??//指針函數的定義
          int*?fun(int?x,int?y)???//指針函數的定義

          以上三種寫法均正確,但是*靠近返回值一點更容易理解。

          指針函數的調用

          在調用指針函數時,需要一個同類型的指針來接收其函數的返回值。

          typedef?struct?_Data{
          ????int?a;
          ????int?b;
          }Data;

          //指針函數
          Data*?f(int?a,int?b){
          ????Data?*?data?=?new?Data;
          ????data->a?=?a;
          ????data->b?=?b;
          ????return?data;
          }

          int?main(int?argc,?char?*argv[])
          {
          ????QApplication?a(argc,?argv);
          ????//調用指針函數
          ????Data?*?myData?=?f(4,5);
          ????qDebug()?<"f(4,5)?=?"?<a?<b;

          ????return?a.exec();
          }

          不過也可以將其返回值定義為 void* 類型,在調用的時候強制轉換返回值為自己想要的類型。

          其輸出結果是一樣的,不過不建議這么使用,因為強制轉換可能會帶來風險。返回類型可以是任何基本類型和復合類型。返回指針的函數的用途十分廣泛。

          事實上,每一個函數,即使它不帶有返回某種類型的指針,它本身都有一個入口地址,該地址相當于一個指針。

          比如函數返回一個整型值,實際上也相當于返回一個指針變量的值,不過這時的變量是函數本身而已,而整個函數相當于一個“變量”。

          4 函數指針與指針函數區(qū)別

          通過以上的介紹,小伙伴應該都能理解二者的定義。那么簡單的總結下二者的區(qū)別:

          1. 定義不同

          • 指針函數本質是一個函數,其返回值為指針。
          • 函數指針本質是一個指針,其指向一個函數。

          2. 寫法不同

          • 指針函數:int* fun(int x,int y);
          • 函數指針:int (*fun)(int x,int y);

          可以簡單粗暴的理解為,指針函數的*是屬于數據類型的,而函數指針的星號是屬于函數名的。

          再簡單一點,可以這樣辨別兩者:函數名帶括號的就是函數指針,否則就是指針函數。

          3. 用法不同

          上面函數指針和指針函數的用法都有,但是函數指針的用法會更多,相對而言難度也更大,例如函數指針與回調函數,如果是C++非靜態(tài)成員函數指針,其用法也會有一些區(qū)別,感興趣的同學可以關注后續(xù)推文或自行查閱相關書籍。

          總而言之,這兩個東西很容易搞混淆,一定要深入理解其兩者定義和區(qū)別,避免犯錯。

          推薦閱讀:


          嵌入式編程專輯

          Linux 學習專輯

          C/C++編程專輯

          關注微信公眾號『技術讓夢想更偉大』,后臺回復“m”查看更多內容,回復“加群”加入技術交流群。

          長按前往圖中包含的公眾號關注

          瀏覽 28
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  无码免费一区二区三区免费播放 | 特黄特色特刺激免费播放 | 五月丁香六月 | www…黄色在线免费观看 x8x8拨牐拨牐精品视频 | 俺去也俺来也在线www官网 |