1. 程式人生 > >setjmp和longjmp函式使用詳解

setjmp和longjmp函式使用詳解

在網上看到的,覺得很有用,copy過來的。

非區域性跳轉語句---setjmp和longjmp函式。非區域性指的是,這不是由普通C語言goto,語句在一個函式內實施的跳轉,而是在棧上跳過若干呼叫幀,返回到當前函式呼叫路徑上的某一個函式中。
#include <setjmp.h>
Int setjmp(jmp_buf  env);
       返回值:若直接呼叫則返回0,若從longjmp呼叫返回則返回非0值的longjmp中的val值
Void longjmp(jmp_buf env,int val);
        呼叫此函式則返回到語句setjmp所在的地方,其中env 就是setjmp中的 env,而val 則是使setjmp的返回值變為val。


        當檢查到一個錯誤時,則以兩個引數呼叫longjmp函式,第一個就是在呼叫setjmp時所用的env,第二個引數是具有非0值的val,它將成為從setjmp處返回的值。使用第二個引數的原因是對於一個setjmp可以有多個longjmp。

#include <stdio.h>
#include <setjmp.h>
 
static jmp_buf buf;
 
void second(void) {
    printf("second\n");         // 列印
    longjmp(buf,1);             // 跳回setjmp的呼叫處 - 使得setjmp返回值為1
}
 
void first(void) {
    second();
    printf("first\n");          // 不可能執行到此行
}
 
int main() {   
    if ( ! setjmp(buf) ) {
        first();                // 進入此行前,setjmp返回0
    } else {                    // 當longjmp跳轉回,setjmp返回1,因此進入此行
        printf("main\n");       // 列印
    }
 
    return 0;
}

上述程式將輸出:

second
main

注意到雖然first()子程式被呼叫,"first"不可能被列印。"main"被列印,因為條件語句if ( ! setjmp(buf) )被執行第二次。 

使用setjmp和longjmp要注意以下幾點:

1、setjmp與longjmp結合使用時,它們必須有嚴格的先後執行順序,也即先呼叫setjmp函式,之後再呼叫longjmp函式,以恢復到先前被儲存的“程式執行點”。否則,如果在setjmp呼叫之前,執行longjmp函式,將導致程式的執行流變的不可預測,很容易導致程式崩潰而退出

2.  longjmp必須在setjmp呼叫之後,而且longjmp必須在setjmp的作用域之內。具體來說,在一個函式中使用setjmp來初始化一個全域性標號,然後只要該函式未曾返回,那麼在其它任何地方都可以通過longjmp呼叫來跳轉到 setjmp的下一條語句執行。實際上setjmp函式將發生呼叫處的區域性環境儲存在了一個jmp_buf的結構當中,只要主調函式中對應的記憶體未曾釋放 (函式返回時區域性記憶體就失效了),那麼在呼叫longjmp的時候就可以根據已儲存的jmp_buf引數恢復到setjmp的地方執行。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

異常處理

在下例中,setjmp被用於包住一個例外處理,類似trylongjmp呼叫類似於throw語句,允許一個異常返回給setjmp一個異常值。下屬程式碼示例遵從1999 ISO C standardSingle UNIX Specification:僅在特定範圍內引用setjmp

  • ifswitch或它們的巢狀使用的條件表示式
  • 上述情況下與!一起使用或者與整數常值比較
  • 作為單獨的語句(不使用其返回值)

遵從上述規則使得建立程式環境緩衝區更為容易。更一般的使用setjmp可能引起未定義行為,如破壞區域性變數;編譯器被要求保護或警告這些用法。但輕微的複雜用法如switch ((exception_type = setjmp(env))) { }在文獻與實踐中是常見的,並保持了相當的可移植性。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
 
void first(void);
void second(void);
 
/* This program's output is:
 
calling first
calling second
entering second
second failed with type 3 exception; remapping to type 1.
first failed, exception type 1
 
*/
 
/* Use a file scoped static variable for the exception stack so we can access
 * it anywhere within this translation unit. */
static jmp_buf exception_env;
static int exception_type;
 
int main() {
    void *volatile mem_buffer;
 
    mem_buffer = NULL;
    if (setjmp(exception_env)) {
        /* if we get here there was an exception */
        printf("first failed, exception type %d\n", exception_type);
    } else {
        /* Run code that may signal failure via longjmp. */
        printf("calling first\n");
        first();
        mem_buffer = malloc(300); /* allocate a resource */
        printf(strcpy((char*) mem_buffer, "first succeeded!")); /* ... this will not happen */
    }
    if (mem_buffer)
        free((void*) mem_buffer); /* carefully deallocate resource */
    return 0;
}
 
void first(void) {
    jmp_buf my_env;
 
    printf("calling second\n");
    memcpy(my_env, exception_env, sizeof(jmp_buf));
    switch (setjmp(exception_env)) {
        case 3:
            /* if we get here there was an exception. */
            printf("second failed with type 3 exception; remapping to type 1.\n");
            exception_type = 1;
 
        default: /* fall through */
            memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */
            longjmp(exception_env, exception_type); /* continue handling the exception */
 
        case 0:
            /* normal, desired operation */
            second();
            printf("second succeeded\n");  /* not reached */
    }
    memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */
}
 
void second(void) {
    printf("entering second\n" ); /* reached */
    exception_type = 3;
    longjmp(exception_env, exception_type); /* declare that the program has failed */
    printf("leaving second\n"); /* not reached */
}

相關推薦

setjmp()longjmp()函式

今天看《unix環境高階程式設計》發現這兩個函式,還挺有用的,在c異常處理、協程等作用很大。 使用方法 我們知道,想要實現函式內的跳轉可以使用goto語句,但如果需要從一個函式跳轉到另一個函式,goto是不能完成的,那該如何實現呢? 函式間跳轉原理 示例程式碼: void

setjmplongjmp函式使用

在網上看到的,覺得很有用,copy過來的。 非區域性跳轉語句---setjmp和longjmp函式。非區域性指的是,這不是由普通C語言goto,語句在一個函式內實施的跳轉,而是在棧上跳過若干呼叫幀,返回到當前函式呼叫路徑上的某一個函式中。#include <setjm

linux——setjmp()longjmp()函式的使用

setjmp()和longjmp()函式       ~~~~~

setjmp()longjmp()函式

之前我們講到了過程活動記錄(AR),那麼如何來操縱AR呢,一個可能的方法是,根據區域性變數的地址進行推算,例如對於上面的a函式,執行a函式時的當前AR地址就是引數i的地址偏移8個位元組,也就是 ((char*)&i) - 8。 然而,不同的C編譯器,以及不同的硬體平臺都會產生不同的AR結構佈局,甚至

mallocfree函式(轉載只是為了查閱方便,若侵權立刪)

malloc和free函式詳解   本文介紹malloc和free函式的內容。   在C中,對記憶體的管理是相當重要。下面開始介紹這兩個函式:     一、malloc()和free()的基本概念以及基本用法: 1、函式原型及說明: void *malloc(lon

linux中alarm函式pause函式例項

轉載原文:https://www.cnblogs.com/yxk529188712/p/4982401.html alarm(time);執行之後告訴核心,讓核心在time秒時間之後向該程序傳送一個定時訊號,然後該程序捕獲該訊號並處理; pause()函式使該程序暫停讓出CPU,但是該函式的暫停

PL/SQL單行函式函式

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Linux 多工程式設計——多程序建立:fork() vfork() 函式

一、fork() 函式詳解 需要的標頭檔案: #include <sys/types.h> #include <unistd.h> pid_t fork(void); 功能: 用於從一個已存在的程序中建立一個新程序,新程序稱為子程序,原程序稱為父程序。

C語言itoa()函式atoi()函式(整數轉字元)

轉自:http://c.biancheng.net/cpp/html/792.html C語言提供了幾個標準庫函式,可以將任意型別(整型、長整型、浮點型等)的數字轉換為字串。 以下是用itoa()函式將整數轉換為字串的一個例子:# include <stdio.h># include

vue2.0專案實戰(4)生命週期鉤子函式

最近的專案都使用vue2.0來開發,不得不說,vue真的非常好用,大大減少了專案的開發週期。在踩坑的過程中,因為對vue的生命週期不是特別瞭解,所以有時候會在幾個鉤子函式裡做一些事情,什麼時候做,在哪個函式裡做,我們不清楚。 下面來總結一下vue的生命週期。 vue生命週期簡介 咱們從上圖可以很明顯的看出

程式設計師必備知識——forkexec函式

        在學習UNIX程式設計時,必須要學習的一個函式為fork函式。fork函式也為面試中必定要問的一個問題,尤其是在BAT的面試中,fork函式相關問題更為面試筆試必考知識點。究其原因:

artTemplate子模板輔助函式

我們知道artTemplate是一個前端模板引擎,可以使我們告別字串拼接渲染的麻煩。關於這個模板的優點和具體用法我就不一個個解釋。我在這裡只針對於其中的子模板和輔助函式並用實際程式碼例子來詳細的解釋一下。先看程式碼和效果圖: <!DOCTYPE html> <

C++中getgetline函式

get函式函式原型有多個,其中比較簡單的2個是:char get(); istream &get(char c);前者提取的字元作為函式值,後者提取的字元賦值給引數c,使用get函式從鍵盤提取自負的格式分別為:cin.get(); cin.get(字元變數c);#in

Lua中日期時間函式

最近在做Lua指令碼的時候,用到了Lua的日期和時間函式,發現其功能強大,學習一下!在Lua中,函式time和data提供了所有的日期和時間的功能,下面將這兩個函式分開學習。 time函式: 如果不帶任何引數呼叫time,它會返回一個數字,表示當前的日期和時間,改數字為

mallocfree函式

 本文介紹malloc和free函式的內容。   在C中,對記憶體的管理是相當重要。下面開始介紹這兩個函式:   一、malloc()和free()的基本概念以及基本用法: 1、函式原型及說明: void *malloc(long NumBytes):該函式分

sql中的round函式cast函式

 <一>:ROUND函式主要返回數字表達式並四捨五入為指定的長度或精度。 語法格式如下: ROUND(numeric_expression,length[,function]) 引數說明: 1.numeric_expression:精確數字或近擬數字資料型別類別的

Lua函數語言程式設計區域性函式

         函數語言程式設計中的函式這個術語不是指計算機中的函式(實際上是Subroutine),而是指數學中的函式,即自變數的對映。也就是說一個函式的值僅決定於函式引數的值,不依賴其他狀態。比如sqrt(x)函式計算x的平方根,只要x不變,不論什麼時候呼叫,呼叫幾次

Socket send函式recv函式

如果傳送應用程式沒有處理這個可讀的訊號,而是在send,那麼這要分兩種情況來考慮,假如是在傳送端收到RST標誌之後呼叫send,send將返回 -1,同時errno設為ECONNRESET表示對端網路已斷開,但是,也有說法是程序會收到SIGPIPE訊號,該訊號的預設響應動作是退出程序,如果忽略該訊號,那麼s

Socket中send()函式recv()函式

1、send函式 int send( SOCKET s, const char FAR *buf, int len, int flags ); 不論是客戶還是伺服器應用程式都用send函式來向TCP連線的另一端傳送資料。 客戶程式一般用send函式向伺服器傳送請求,而伺

kzallockmalloc函式

用kzalloc申請記憶體的時候, 效果等同於先是用 kmalloc() 申請空間 , 然後用 memset() 來初始化 ,所有申請的元素都被初始化為 0. /**   * kzalloc - allocate memory. The memory is set to zero.   * @si