C語言下的setjmp longjmp(C 語言異常處理)
在C 語言中,我們不能使用 goto 語句來跳轉到另一個函式中的某個 label 處;但提供了兩個函式——setjmp 和 longjmp來完成這種型別的分支跳轉。後面我們會看到這兩個函式在處理異常上面的非常有用。
setjmp 和 longjmp 使用方法
在一個函式內進行跳轉,可以使用 goto 語句(幾乎所有國內教材都一刀切地教大家儘量不要使用它,但在我看來,這根本不是語言的問題,而是使用該語言的人,看看 Linux 核心中遍地是 goto 語句的應用吧!)但如果從一個函式內跳轉到另一個函式的某處,goto 是不能完成的,那該如何實現呢?
函式間跳轉原理
我們要實現的一個 GOTO 語句(我自己定義的),能實現在函式間進行任意跳轉,如下例,在函式 g() 中有條語句GOTO Label; 可以跳轉到 f() 函式的 Label: 標籤所指向的位置,那麼我們該如何實現呢?
void f()
{
//...
Label:
//...
}
void g()
{
//...
GOTO Label;
//...
}
首先我們要知道,實現這種型別的跳轉,和作業系統中任務切換的上下文切換有點類似,我們只需要恢復 Label 標籤處函式上下文即可。函式的上下文包括以下內容:
- 函式棧幀,主要是棧幀指標BP和棧頂指標SP
- 程式指標PC,此處為指向 Label 語句的地址
- 其它暫存器,這是和體系相關的,在 x86 體系下需要儲存有的 AX/BX/CX 等等 callee-regs。
這樣,在執行 GOTO Label; 這條語句,我們恢復 Label 處的上下文,即完成跳轉到 Label 處的功能。
如果你讀過 Linux 作業系統程序切換的原始碼,你會很明白 Linux 會把程序的上下文儲存在 task_struct 結構體中,切換時直接恢復。這裡我們也可以這樣做,將 Label 處的函式上下文儲存在某個結構體中,但執行到 GOTO Label 語句時,我們從該結構體中恢復函式的上下文。
這就是函式間進行跳轉的基本原理,而 C 語言中 setjmp 和 longjmp 就為我們完成了這樣的儲存上下文和切換上下文的工作。
函式原型
#include <setjmp.h>
int setjmp(jmp_buf env);
setjmp 函式的功能是將函式在此處的上下文儲存在 jmp_buf 結構體中,以供 longjmp 從此結構體中恢復。
引數 env 即為儲存上下文的 jmp_buf 結構體變數;
如果直接呼叫該函式,返回值為 0; 若該函式從 longjmp 呼叫返回,返回值為非零,由 longjmp 函式提供。根據函式的返回值,我們就可以知道 setjmp 函式呼叫是第一次直接呼叫,還是由其它地方跳轉過來的。
void longjmp(jmp_buf env, int val);
longjmp 函式的功能是從 jmp_buf 結構體中恢復由 setjmp 函式儲存的上下文,該函式不返回,而是從 setjmp 函式中返回。
- 引數 env 是由 setjmp 函式儲存過的上下文。
- 引數 val 表示從 longjmp 函式傳遞給 setjmp 函式的返回值,如果 val 值為0, setjmp 將會返回1,否則返回 val。
longjmp 不直接返回,而是從 setjmp 函式中返回,longjmp 執行完之後,程式就像剛從 setjmp 函式返回一樣。
#include <setjmp.h>
int main()
{
237 jmp_buf env;
238 int i;
239
240 i = setjmp(env);
241 printf("i=%d\n", i);
242 if (i!=0) exit(0);
243 longjmp(env, 3);
244 printf("This line does not get printed\n");
}
程式執行結果:
i = 0
i = 3
C 語言異常處理
Java、C# 等面嚮物件語言中都有異常處理的機制,如下就是典型的 Java 中異常處理的程式碼,兩個數相除,如果被除數為0丟擲異常,在函式 f() 中可以獲取該異常並進行處理:
double divide(double to, double by) throws Bad {
if(by == 0)
throw new Bad ("Cannot / 0");
return to / by;
}
void f() {
try {
divide(2, 0);
//...
} catch (Bad e) {
print(e.getMessage());
}
print("done");
}
在 C 語言中雖然沒有類似的異常處理機制,但是我們可以使用 setjmp 和 longjmp 來模擬實現該功能,這也是這兩個函式的一個重要的應用:
static jmp_buf env;
double divide(double to, double by)
{
if(by == 0)
longjmp(env, 1);
return to / by;
}
void f()
{
if (setjmp(env) == 0)
divide(2, 0);
else
printf("Cannot / 0");
printf("done");
}
如果複雜一點,可以根據 longjmp 傳遞的返回值來判斷各種不同的異常,來進行區別的處理,程式碼結構如下:
switch(setjmp(env)):
case 0: //default
//...
case 1: //exception 1
//...
case 2: //exception 2
//...
//...
關於使用 C 語言來處理異常,可以參見這篇文章,介紹了更多複雜的結構,但無外乎就是 setjmp 和 longjmp 的應用。
相關推薦
C語言下的setjmp longjmp(C 語言異常處理)
在C 語言中,我們不能使用 goto 語句來跳轉到另一個函式中的某個 label 處;但提供了兩個函式——setjmp 和 longjmp來完成這種型別的分支跳轉。後面我們會看到這兩個函式在處理異常上面的非常有用。 setjmp 和 longjmp 使
C++語言學習(十八)——異常處理
right data ges cal 修飾符 當前 ins 最終 cati C++語言學習(十八)——異常處理 一、C語言異常處理 異常是指程序在運行過程中產生可預料的執行分支。如除0操作,數組訪問越界、要打開的文件不存在。Bug是指程序中的錯誤,是不被預期的運行方式。如野
Java語言中的----多態和異常處理
java語言中的----多態和異常處理day13 Java語言中的----多態和異常處理一、概述: 學習Java語言就要理解Java中的面向對象的四大特征:分別是封裝、抽象、繼承、多態。前面三個我們已經學完了,下面我們來學一下多態。什麽是多態?不明思議就是具有兩種形態性,一個方法可以定義兩種形態,這就是
Java語言學習(九):異常處理
異常是程式中的一些錯誤,但並不是所有的錯誤都是異常,並且錯誤有時候是可以避免的。常見的三種異常型別有: 檢查性異常,如開啟一個不存在的檔案 執行時異常,如陣列越界 錯誤,如棧溢位 &
20.Java語言IO流、IO流異常處理、以及Properties
IO流 I :Input(輸入)資料從外部流入程式(硬碟到記憶體) O:Output(輸出)資料從程式流出外部(記憶體到硬碟) 流:類似於水流—有方向,線性 作用: 可以讀寫檔案的內容 體系: A).位元組流:按“位元組”讀寫檔案。可以操作任何型別檔案
Go語言學習筆記(十五)之異常處理
22.異常處理 error介面定義如下: 1: type error interface { 2: Error() string 3: } Go語言的標準庫程式碼包errors為使用者提供如下方法: 1: package errors 2: 3: type errorStrin
C++ 構造/解構函式中的異常處理
C++ 為什麼會引入(需要)異常? The C++ Programming Language: 一個庫的作者可以檢測出發生了執行時錯誤,但一般不知道怎樣去處理它們(因為和使用者具體的應用有關);另一方面,庫的使用者知道怎樣處理這些錯誤,但卻無法檢查它們何時發生(如果能
C++中讀寫檔案過程中異常處理機制
在利用C++進行檔案讀取與寫入過程中,無論是針對二進位制檔案還是文字檔案均需要進行異常處理,在C++中我們可以利用CFile進行檔案的讀寫,而在MFC中還可以利用CStdioFile進行檔案的讀寫。 利用CFile進行讀文字檔案過程中的異常處
Perl語言入門:檔案操作和異常處理初步
Perl使用一種叫“檔案控制代碼”的變數來操作檔案 在文章末尾將補充講解Perl5新加入的語法特性 檔案控制代碼 檔案控制代碼(filehandle)是檔案操作中用來存放檔案唯一識別符號的名稱空間 或者說,檔案控制代碼是一個I/O連線的名
C語言異常處理之 setjmp()和longjmp()
divide font 變量 bsp ron 否則 sharp highlight == 異常處理之除0情況 相信大家處理除0時,都會通過函數,然後判斷除數是否為0,代碼如下所示: double divide(doublea,double b) { co
C語言中利用setjmp和longjmp做異常處理
錯誤處理是任何語言都需要解決的問題,只有不能保證100%的正確執行,就需要有處理錯誤的機制。異常處理就是其中的一種錯誤處理方式。 1 過程活動記錄(Active Record) C語言中每當有一個函式呼叫時,就會在堆疊(Stack)上準備一個被稱為AR的結構
C語言中的異常處理機制
軟件測試 如何實現 char* oar 朋友 核心 初始化 flag out #define try if(!setjmp(Jump_Buffer)) 返回try現場後重新執行判斷,所以有兩次執行。 http://blog.csdn.net/tian_dao_chou_q
8、C#語言裏面的異常處理
c#在C#語言裏面的異常處理,和Java語言的異常處理,幾乎是如出一轍。都是由:try、catch、finally這幾個關鍵詞組成。第一種異常處理是由try和catch組成。舉例如下://在進行除法運算的時候,除數不能為0,否則會發生異常。try{int 除數;System.Console.Write("請輸
linux信號解釋(4)--C語言下的理解
linux信號 C語言下linux信號理解 上一節中中簡單介紹了信號的處理機制,就是調用函數庫來實現信號的處理,因此,在這節中,介紹在C語言下如何理解信號的處理機制。 創建一個文件signal.c,文件內容如下:(對於學過一下C語言的童鞋來說是不是很熟悉呢) #include<signal.h&
C 語言異常處理(五十二)
異常處理 C 中的異常處理 if...else... setjmp() longjmp() 我們今天來看下異常處理,在看 C++ 的異常處理之前,先來看看 C 語言中的異常處理。那麽什麽是異常呢?在程序運行過程中可能會產生異常,異常(Exception)與 Bug 的區別是
C# Winform下一個熱插拔的MIS/MRP/ERP框架(多語言方案)
文件加載 全局 查詢 分享 技術 變量 支持 對象 style 個別時候,我們需要一種多語種切換方案。 我的想方案是這樣的: 1、使用文本文本存儲多語言元素,應用程序啟動時加載到內存表中; 2、應用程序啟動時從配置文件加載語種定義; 3、所有窗體繼承自一個Base
Ubuntu下GDB除錯C語言程式
轉自:http://zhgeaits.me/other/2013/03/17/gdb-study-notes.html,感謝博主。 1.GDB是什麼 GDB是GNU開源組織釋出的一個UNIX下的程式除錯工具,專門用來除錯C,C++這些程式的了,而且都是命令列模式的。 2.準備工作 平
C++筆記 第六十三課 C語言異常處理---狄泰學院
如果在閱讀過程中發現有錯誤,望評論指正,希望大家一起學習,一起進步。 學習C++編譯環境:Linux 第六十三課 C語言異常處理 1.異常處理 異常的概念 程式在執行過程中可能產生異常 異常(Exception)與Bug的區別 異常是程式執行時可預料的執行分支 Bug是程式中
在Linux(Ubuntu16.04)下編寫執行C語言程式
最初學習C語言用的是Visual C++6.0,後來還用過一些IDE,複製貼上都可以用滑鼠對目標進行選擇即可。但在Linux系統裡,需要熟練掌握在Terminal裡編寫C語言程式,進行編譯除錯。本章
c語言丟擲異常處理程式碼
try catch在java和c++中是有現成實現的,但是在c語言中是買有的,下面實現是來自網路上其他人提供的巨集定義方法,該方法有一定的侷限性,但是也有不少啟發。 下面是一段例子程式碼,需要使用的人可以自行修改。 #include <stdio.h> #in