1. 程式人生 > >C語言下的setjmp longjmp(C 語言異常處理)

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語言中利用setjmplongjmp異常處理

錯誤處理是任何語言都需要解決的問題,只有不能保證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

UbuntuGDB除錯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