c語(yǔ)言中的局部跳轉(zhuǎn)及全局跳轉(zhuǎn)功能
一、前言
在c語(yǔ)言中,當(dāng)我們?cè)谔幚砟承┊惓G闆r的時(shí)候,經(jīng)常會(huì)使用goto語(yǔ)句來(lái)進(jìn)行跳轉(zhuǎn)。goto用起來(lái)很方便,但可能很多人都不知道,goto只能在一個(gè)函數(shù)里面跳轉(zhuǎn),并不能夠跨函數(shù)跳轉(zhuǎn)。本文將介紹能夠跨函數(shù)跳轉(zhuǎn)的接口setjmp和longjmp,將圍繞如下內(nèi)容展開:
1.goto的局限性
2.進(jìn)程運(yùn)行時(shí)的棧幀結(jié)構(gòu)
3.setjmp和longjmp簡(jiǎn)介和使用方法
4.使用了全局跳轉(zhuǎn)后main中變量的狀態(tài)
二、goto的局限性
goto只能在同一個(gè)函數(shù)里面跳轉(zhuǎn),無(wú)法從一個(gè)函數(shù)跳轉(zhuǎn)到另一個(gè)函數(shù)中。如果試圖從一個(gè)函數(shù)跳轉(zhuǎn)到另一個(gè)函數(shù),則編譯會(huì)報(bào)錯(cuò)。參考代碼如下:
/************************************************************************* > File Name: goto_test.c > Author: conbiao > Created Time: 2024年09月13日 星期五 10時(shí)47分41秒 ************************************************************************/ /*********************************************************************** * HEADER **********************************************************************/ #include<stdio.h> /*********************************************************************** * MACRO **********************************************************************/ /*********************************************************************** * GLOBAL VARIABLE **********************************************************************/ /*********************************************************************** * FUNCTION DESCRIPTION **********************************************************************/ void goto_test(void); /*********************************************************************** * FUNCTION NAME: void goto_test() *********************************************************************** * * Summary: * This function is used to test goto. * * Params: * NONE. * * Return: * NONE. * ***********************************************************************/ void goto_test(void) { int i; start: i = 0; printf("%s: start!\n",__func__); while(1) { printf("%s: i = %d\n",__func__,i++); if(i > 5) goto main_start; } } /*********************************************************************** * MAIN **********************************************************************/ int main(int argc, char *argv[]) { int ret = 0; main_start: printf("%s: start!\n",__func__); goto_test(); return ret; }
編譯結(jié)果如下:
(2-1)
如果在同一個(gè)函數(shù)中使用goto,則能夠正常跳轉(zhuǎn),代碼如下:
/************************************************************************* > File Name: goto_test.c > Author: conbiao > Created Time: 2024年09月13日 星期五 10時(shí)47分41秒 ************************************************************************/ /*********************************************************************** * HEADER **********************************************************************/ #include<stdio.h> /*********************************************************************** * MACRO **********************************************************************/ /*********************************************************************** * GLOBAL VARIABLE **********************************************************************/ /*********************************************************************** * FUNCTION DESCRIPTION **********************************************************************/ void goto_test(void); /*********************************************************************** * FUNCTION NAME: void goto_test() *********************************************************************** * * Summary: * This function is used to test goto. * * Params: * NONE. * * Return: * NONE. * ***********************************************************************/ void goto_test(void) { int i; start: i = 0; printf("%s: start!\n",__func__); while(1) { printf("%s: i = %d\n",__func__,i++); if(i > 5) goto start; } } /*********************************************************************** * MAIN **********************************************************************/ int main(int argc, char *argv[]) { int ret = 0; main_start: printf("%s: start!\n",__func__); goto_test(); return ret; }
運(yùn)行結(jié)果如下:
(2-2)
三、進(jìn)程運(yùn)行時(shí)的棧幀結(jié)構(gòu)
在https://editor.csdn.net/md/?articleId=142054973一節(jié)中已經(jīng)介紹過(guò)c程序運(yùn)行時(shí)的存儲(chǔ)空間布局,這其實(shí)是一個(gè)進(jìn)程的進(jìn)程空間結(jié)構(gòu)。其中,??臻g用于存儲(chǔ)程序在運(yùn)行過(guò)程中使用到的臨時(shí)變量,在進(jìn)程空間中,系統(tǒng)為每個(gè)函數(shù)分配一個(gè)棧幀,結(jié)構(gòu)如下:
goto只能在其調(diào)用函數(shù)的棧幀中跳轉(zhuǎn),無(wú)法從一個(gè)棧幀跳轉(zhuǎn)到另一個(gè)棧幀。
四、setjmp和longjmp
使用setjmp和longjmp能夠?qū)崿F(xiàn)在棧幀上進(jìn)行跳躍,其中setjmp用于設(shè)置標(biāo)記,它會(huì)保存當(dāng)前的執(zhí)行環(huán)境(如程序計(jì)數(shù)器、棧指針等)在調(diào)用longjmp后,會(huì)恢復(fù)之前保存的執(zhí)行環(huán)境,相當(dāng)于跳轉(zhuǎn)到setjmp的位置,。
4.1 setjmp
setjmp的函數(shù)實(shí)現(xiàn)如下:
頭文件:#include <setjmp.h>
函數(shù)原型: int setjmp(jmp_buf env);
返回值:
如果 setjmp 是直接調(diào)用的,它將返回 0。
如果 setjmp 是通過(guò) longjmp 調(diào)用的,它將返回 longjmp 的第二個(gè)參數(shù)(非零值)。
傳入?yún)?shù):
jmp_buf env:這是一個(gè)用于存儲(chǔ)執(zhí)行環(huán)境的數(shù)組類型。setjmp 會(huì)將當(dāng)前的執(zhí)行環(huán)境保存到這個(gè)數(shù)組中。
4.2 longjmp
longjmp的函數(shù)原型如下:
頭文件: #include <setjmp.h> 函數(shù)原型: void longjmp(jmp_buf env, int val);
傳入?yún)?shù):
jmp_buf env:這是之前由 setjmp 保存的執(zhí)行環(huán)境。
int val:這是 longjmp 返回給 setjmp 的值。通常是一個(gè)非零值,用于區(qū)分不同的跳轉(zhuǎn)點(diǎn)。
4.3 參考代碼
/************************************************************************* > File Name: jump_test.c > Author: conbiao > Created Time: 2024年09月13日 星期五 14時(shí)24分11秒 ************************************************************************/ /*********************************************************************** * HEADER **********************************************************************/ #include<stdio.h> #include<setjmp.h> /*********************************************************************** * MACRO **********************************************************************/ /*********************************************************************** * GLOBAL VARIABLE **********************************************************************/ jmp_buf env; static int i = 0; /*********************************************************************** * FUNCTION DESCRIPTION **********************************************************************/ void func1(void); /*********************************************************************** * FUNCTION NAME: *********************************************************************** * * Summary: * * Params: * * Return: * ***********************************************************************/ void func1(void) { printf("%s: start!\n",__func__); while(i < 8) { longjmp(env,i++); } } /*********************************************************************** * MAIN **********************************************************************/ int main(int argc, char *argv[]) { int ret = 0; ret = setjmp(env); if(ret == 0) { printf("%s: This is the first call setjump!\n",__func__); } else { printf("%s: This is return from longjmp! ret = %d\n",__func__,ret); } func1(); return ret; }
運(yùn)行結(jié)果如下:
(4.3-1)
可見,使用setjmp和longjmp實(shí)現(xiàn)了從func1的棧幀跳轉(zhuǎn)到main函數(shù)所在的棧幀的功能。
分析一下上面代碼的執(zhí)行過(guò)程,在main函數(shù)調(diào)用setjmp前,該進(jìn)程的棧如下:
(4.3-2)
到執(zhí)行func1函數(shù),調(diào)用longjmp前,??臻g如下:
(4.3-3)
在setjmp時(shí),會(huì)將當(dāng)前的執(zhí)行環(huán)境信息保存到env中,當(dāng)執(zhí)行l(wèi)ongjmp時(shí),??臻g會(huì)回到4.3-2的情況,func1的棧幀就沒有了。
五、使用全局跳轉(zhuǎn)后main函數(shù)中變量的狀態(tài)
上文已經(jīng)說(shuō)明了,使用longjmp會(huì)使程序回退到setjmp前的狀態(tài)。那么,如果main函數(shù)中在調(diào)用setjmp前定義了一個(gè)變量,在調(diào)用setjmp后改變了這個(gè)變量的值,那么當(dāng)調(diào)用longjmp后,該變量的值是調(diào)用setjmp前的還是后的呢?
c標(biāo)準(zhǔn)表示這個(gè)狀態(tài)是不確定的。
如果希望調(diào)用longjmp后該變量的值不變,那么可以將該變量定義為全局變量或者使用static修飾,亦或者使用volatile屬性聲明。
參考資料:
《UNIX環(huán)境高級(jí)編程(第3版) (史蒂文斯 (W.Richard Stevens) 拉戈 (Stephen A.Rago))
(Z-Library)》
到此這篇關(guān)于c語(yǔ)言中的局部跳轉(zhuǎn)以及全局跳轉(zhuǎn)的文章就介紹到這了,更多相關(guān)c語(yǔ)言跳轉(zhuǎn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)選票統(tǒng)計(jì)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)選票統(tǒng)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07解析內(nèi)存對(duì)齊 Data alignment: Straighten up and fly right的詳解
對(duì)于所有直接操作內(nèi)存的程序員來(lái)說(shuō),數(shù)據(jù)對(duì)齊都是很重要的問題.數(shù)據(jù)對(duì)齊對(duì)你的程序的表現(xiàn)甚至能否正常運(yùn)行都會(huì)產(chǎn)生影響2013-05-05用C# 控制Windows系統(tǒng)音量的實(shí)現(xiàn)方法
本篇文章是對(duì)使用C#控制Windows系統(tǒng)音量的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C++實(shí)現(xiàn)班車管理系統(tǒng)課程設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)班車管理系統(tǒng)課程設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03C++ 中時(shí)間與時(shí)間戳的轉(zhuǎn)換實(shí)例詳解
這篇文章主要介紹了C++ 中時(shí)間與時(shí)間戳的轉(zhuǎn)換實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06C語(yǔ)言模擬實(shí)現(xiàn)字符串庫(kù)函數(shù)的示例講解
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言模擬實(shí)現(xiàn)字符串庫(kù)函數(shù)的具體方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01C++實(shí)現(xiàn)頭文件保護(hù)機(jī)制
頭文件保護(hù)機(jī)制是一種防止頭文件被重復(fù)包含的技術(shù),它主要借助 #ifndef、#define 和 #endif 這些預(yù)處理指令來(lái)達(dá)成,本文就來(lái)詳細(xì)的介紹一下,感興趣的可以了解一下2025-04-04