深刻進修C說話中的函數指針和閣下軌則。本站提示廣大學習愛好者:(深刻進修C說話中的函數指針和閣下軌則)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻進修C說話中的函數指針和閣下軌則正文
平日的函數挪用
一個平日的函數挪用的例子:
//自行包括頭文件 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*的關系了?(有無一點點的懵懂了?)
看來與之前的代碼有點抵觸了,是吧!所以我說嘛!
請允許我暫不給你說明,持續看以下幾種情形(這些可都是可以准確運轉的代碼喲!):
代碼之三:
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; }
真的是可以如許的噢!
(哇!真是要暈倒了!)
還有吶!看——
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; }
依據正文,應當不好看懂吧!(固然你能夠很少如許界說應用,但今後進修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是一個指向數組的指針,如今往左看,右邊有一個*號,解釋這個數組的元素是指針,再跳出括號,向右看,左邊又有一個括號,解釋這個數組的元素是指向函數的指針。總結一下就是:func是一個指向數組的指針,這個數組的元素是函數指針,這些指針具有int *形參,前往值為int類型的函數
int (*(*func)(int *p))[5];
func是一個函數指針,這類函數具有int *類型的形參,前往值是指向數組的指針,所指向的是具有5個int類型元素的數組。