相信大家看到這個(gè)標(biāo)題,,可能已經(jīng)猜到本文要談的話題了。沒(méi)錯(cuò),,今天給大家介紹一種比goto還要“任性”的跳轉(zhuǎn)方式,,那就是C函數(shù)庫(kù)中的如下兩個(gè)函數(shù):1//所需頭文件 2#include <setjmp.h> 3 4int setjump(jmp_buf buf) 5void longjump(jmp_buf buf, int i) 6
一些朋友該說(shuō)了,“我從來(lái)不用這些跳轉(zhuǎn),,免得出問(wèn)題”,。下面,,我們來(lái)看看這兩個(gè)函數(shù)到底有什么可以推敲的東西。 有研究過(guò)RTOS的朋友應(yīng)該對(duì)此不難理解,,setjump主要是保存當(dāng)前函數(shù)調(diào)用點(diǎn)的現(xiàn)場(chǎng)環(huán)境(或者叫上下文),,比如各種寄存器、堆棧等等,,那么這些環(huán)境信息就記錄在jmp_buf所定義的buf中,。 而當(dāng)我們?cè)谄渌恢谜{(diào)用longjump函數(shù)就相當(dāng)于一個(gè)長(zhǎng)跳轉(zhuǎn),傳入之前保存在buf中的信息,,即可跳回到之前setjump所調(diào)用的位置(理解為恢復(fù)setjump所保存的環(huán)境也是可以的),。 所以,這里值得注意的是,,不要率先調(diào)用longjump,,否則程序不知道飛去哪里了。 其實(shí)跟RTOS中進(jìn)行任務(wù)切換有著異曲同工之妙,。 你大概已經(jīng)注意到setjump有一個(gè)返回值,,其主要分為兩種情況: 當(dāng)直接調(diào)用setjump函數(shù),則返回0,; 當(dāng)調(diào)用longjump跳轉(zhuǎn)到setjump位置,,則其返回longjump的第二個(gè)非零參數(shù)。
以前我也跟大家介紹過(guò)goto這匹野馬被馴服的方式(goto關(guān)鍵字你不知道的'那些事'(C語(yǔ)言提升)),,在C語(yǔ)言中g(shù)oto只能實(shí)現(xiàn)函數(shù)內(nèi)部的跳轉(zhuǎn),無(wú)法實(shí)現(xiàn)跨函數(shù)的直接跳轉(zhuǎn),,比如函數(shù)嵌套多層的跳轉(zhuǎn)等等,。 當(dāng)然,你也可以借助goto與函數(shù)返回配合完成函數(shù)之間的跳轉(zhuǎn),,不過(guò)那太麻煩了,,所以這兩個(gè)庫(kù)函數(shù)該派上用場(chǎng)了。 這樣的跳轉(zhuǎn)太過(guò)于霸道,,我們還是限制一下,,切不可濫用,,但其為異常處理代碼的模塊化帶來(lái)了福音,在非常多的開源庫(kù)中都有實(shí)際應(yīng)用,。 下面給大家一個(gè)參考示例 : 1#include <stdio.h> 2#include <setjmp.h> 3 4jmp_buf mark; 5int fperr; 6void fpcheck(void); 7 8/********************************************* 9 * Function: main 10 * Description : 主任務(wù)函數(shù) 11 * Note:(公眾號(hào):最后一個(gè)bug) 12 *********************************************/ 13int main( void ) 14{ 15 int jmpret; 16 17 //記錄異常代碼與正常代碼分支位置 18 jmpret = setjmp(mark); 19 if( jmpret == 0 ) 20 { 21 //正常用戶程序運(yùn)行 22 23 } 24 else 25 { 26 //在正常用戶程序運(yùn)行過(guò)程中發(fā)生異常 27 fpcheck(); 28 } 29} 30/********************************************* 31 * Function: Errorhandler 32 * Description : 異常中斷,,在正常用戶程序運(yùn)行過(guò)程中發(fā)生異常處理函數(shù) 33 * Note:(公眾號(hào):最后一個(gè)bug) 34 *********************************************/ 35void Errorhandler(void) 36{ 37 fperr = num; 38 longjmp( mark, -1 ); //進(jìn)行長(zhǎng)跳轉(zhuǎn)到異常處理 39} 40 41/********************************************* 42 * Function: fpcheck 43 * Description : 故障處理函數(shù) 44 * Note:(公眾號(hào):最后一個(gè)bug) 45 *********************************************/ 46void fpcheck(void) 47{ 48 49 switch( fperr ) 50 { 51 case INVALID: 52 //user Code 53 break; 54 55 case OVERFLOW: 56 //user Code 57 break; 58 59 case ZERODIVIDE: 60 //user Code 61 break; 62 default: 63 break; 64 } 65 66}
這組函數(shù)除了前面介紹的注意事項(xiàng),還有一個(gè)非常值得注意的點(diǎn)就是longjump的調(diào)用時(shí)機(jī)必須在setjump被調(diào)用的所在函數(shù)返回前,。 因?yàn)閟etjump保存有堆棧信息等,,一旦setjump的被調(diào)用的函數(shù)返回則相應(yīng)的環(huán)境會(huì)被釋放,導(dǎo)致longjump無(wú)法在恢復(fù)到setjump調(diào)用位置,,可能造成程序奔潰,。好了,今天就跟大家分享這么多了,,這一塊還有一些東西可以挖掘,,后面再整理一下分享出來(lái)。如果你覺(jué)得有所收獲,,一定記得點(diǎn)個(gè)贊,!
|