91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

C語言的異常處理案例代碼

m3eY_edn_china ? 來源:未知 ? 作者:易水寒 ? 2017-12-22 08:44 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

相信很多朋友在此之前可能根本沒有使用或者聽說過C語言的異常處理,印象中都是C++或者java才有的東西,C語言怎么會有異常處理呢?

當然估計在大學出于一般的性的學習考試之類的話老師幾乎是不會提及C語言的異常處理的,那么到底什么是異常處理?C語言中又該如何來實現異常處理呢?

那么我們今天就說說一種典型的實現C語言異常處理的方法,以setjmp()函數和longjmp()函數實現的異常處理,我盡可能的把它們是怎樣實現異常處理方法講解清楚,希望接下來的內容對你有所幫助,讓你學到一些新的東西。

首先我們來了解下異常處理,異常是一個在程序執(zhí)行期間發(fā)生的事件,它中斷正在執(zhí)行的程序的正常的指令流,而我們的異常處理功能提供了處理程序運行時出現的任何意外或異常情況的方法。

接下來我們先看看setjmp()函數和longjmp()函數實現C語言異常處理。

setjmp()函數原型:

int ( jmp_buf env );

如果我們打開源代碼會發(fā)現在setjmp()函數中涉及到很多的寄存器的操作,如Ebp、Ebx、Edi、Esi、Esp、 Eip等等,在此就不一一例舉了,我們無非是想向讀者說明一個問題,那就是在調用setjmp()函數的過程中保存程序的當前運行時的堆棧環(huán)境,保存這些堆棧環(huán)境有什么用呢?接下來我們看看longjmp()函數。

longjmp()函數原型:

void longjmp( jmp_buf env, int value );

剛剛上面的函數功能是保存程序執(zhí)行時候的堆棧環(huán)境,我們發(fā)現在longjmp()函數里也有一個jmp_buf類型的env變量,這其實是為了保證接下來調用longjmp時,會根據這個曾經保存的變量來恢復先前的環(huán)境,并且當前的程序控制流,會因此而返回到最初調用setjmp()函數時的程序執(zhí)行點。此時,在接下來的控制流的例程中,所能訪問的所有的變量,包含了longjmp函數調用時所擁有的變量。我們就這樣說讀者可能就得有點抽象了,那我們還是來看看一段代碼后再來分析吧,在此特地給出了一個簡單的代碼,由易到難的來分析。

[cpp] view plaincopy

#include

#include

jmp_buf buf;

void error_code(void)

{

longjmp(buf,1);

}

int main()

{

double a,b;

printf("請輸入被除數:");

scanf("%lf",&a);

printf("請輸入除數:");

if(setjmp(buf)==0)

{

scanf("%lf",&b);

if(0==b)

error_code();

printf("相除的結果為:%f\n",a/b);

}

else

printf("出現錯誤除數為0\n");

return 0;

}

運行結果為:

[cpp] view plaincopy

請輸入被除數:12

請輸入除數:0

出現錯誤除數為0

Press any key to continue

看了上面的運行結果,現在我們接著上面的講,在一開始的部分我們并沒有具體的交代setjmp()函數和longjmp()函數的返回值和參數的具體含義。兩個函數中的env變量保存的是調用setjmp()函數的時候當前運行程序的堆棧信息,而longjmp()函數的調用就是根據在調用setjmp()函數的時候的堆棧信息返回到最初調用setjmp()函數的地方,而其中的第二個參數就是此刻setjmp()函數的返回值,但是值得注意的就是調用longjmp()函數之后setjmp函數返回的值必須是非零值,如果longjmp傳送的value參數值為0,那么實際上setjmp返回的值是1。一開始我們調用setjmp()函數的時候,它的返回值為0,之后再調用longjmp()函數的時候,通過設定longjmp()函數的第二個參數來設定它的返回值。

現在我們來分析上邊的代碼,在main()函數中,我們最初調用setjmp()函數的時候,把當前的環(huán)境信息保存在了buf中,函數返回0,然后往下運行,我們輸入0。通過if語句發(fā)現b的值為0那么就調用error_code()函數來進行處理,在該函數中我們使用了longjmp()函數,其使用方式為longjmp(buf,1);,通過上面的講解,我們知道第一個參數的作用是用來得到最初調用setjmp()函數是的環(huán)境信息,以便在使用longjmp()函數的時候能夠正確的返回到setjmp()函數最初的調用處,而后面的參數表示的返回到setjmp()函數的時候的返回值。我們在此返回1,所以執(zhí)行else部分的語句。

分析完了上面的代碼,讀者應該都知道了兩個函數的使用方法,值得注意的地方就是我們在setjmp與longjmp結合使用時,它們必須有嚴格的先后執(zhí)行順序,先調用setjmp函數,之后再調用longjmp函數,以恢復到先前被保存的“程序執(zhí)行點”。否則,假如在setjmp調用之前,執(zhí)行l(wèi)ongjmp函數,將導致程序的執(zhí)行流變的不可猜測,很輕易導致程序崩潰而退出。為了加深讀者的對于兩個函數參數的使用,我們看看下面的代碼:

[cpp] view plaincopy

#include

#include

#include

#include

jmp_buf buf;

void func1()

{

longjmp(buf,1);

}

void func2()

{

longjmp(buf,2);

}

void func3()

{

longjmp(buf,3);

}

int main( void )

{

int value;

char str[50];

value = setjmp( buf );

if( value == 0 )

{

func1();

}

switch( value )

{

case 1:

strcpy( str, "func1 return value" );

break;

case 2:

strcpy( str, "func2 return value" );

break;

case 3:

strcpy( str, "func3 return value" );

break;

default:

strcpy( str, "Other error value" );

break;

}

printf("%s:%d\n",str,value);

if(1==value)

{

func2();

}

if(2==value)

{

func3();

}

return 0;

}

運行結果為:

[cpp] view plaincopy

func1 return value:1

func2 return value:2

func3 return value:3

Press any key to continue

看看運行結果,我們分析下代碼,在每個函數中我們調用longjmp()函數,通過設置第二個參數為不同的值來改變setjmp()函數的返回值,然后我們通過判斷value值來打印出是那個函數的返回值,我們在此例舉這個簡單的代碼是要大家加深對于這兩個函數的參數的使用情況。如果我們在上面的代碼中稍作修改,在setjmp()函數的調用之前調用longjmp()函數,我們發(fā)現此時沒有任何的輸出,程序直接崩潰掉退出了。

接下來我們來看看一個函數的使用,如果對于這個函數不理解的讀者,可以多看幾次我給出的模擬該函數的實現代碼。

頭文件: #include

功能:設置某一信號的對應動作

函數原型:void (*signal(int signum,void(* handler)(int)))(int);

注意:第一個參數signum指明了所要處理的信號類型,它可以取除了SIGKILL和SIGSTOP外的任何一種信號。

如果讀者是第一場接觸上面的函數的話可能有些不知道該如何著手,一時間有些難以理解,不知道到底是什么意思。別急,我們現在來逐一分析它到底是什么意思,我們在講解之前再來看看它的另外一種表示方法。

typedef void(*sig_t) ( int );

sig_t signal(int signum,sig_t handler);

把上面的函數原型拆分為了如上兩行代碼,現在我們分析下上面的兩行代碼。

第一行代碼定義了一個函數指針(注:如果有對函數指針知識點不熟悉的讀者可以去閱讀我之前寫的那篇文章《C語言的那些小秘密之函數指針》),其類型為含有一個int型參數,無返回值;

第二行代碼中,signal函數的返回值是一個函數指針,與第一行我們定義的類型相同,第二個參數也為一個函數指針,其實signal的返回值就是第二個函數指針指向的函數地址。這樣說可能有不少讀者都有些懵的感覺,還是老方法,代碼最有說服力,我們還是為讀者模擬下signal的實現方式,呈現出一段代碼來分析下。

[cpp] view plaincopy

#include

#include

typedef void (*pfun) ();

pfun signal_call(int a,pfun fdsa);

pfun signal_call(int a,pfun fdsa)

{

return fdsa;

}

void func()

{

printf("hello world!!!\n");

}

int main()

{

pfun p = func;

signal_call(1,p)();

return 0;

}

運行結果為:

[cpp] view plaincopy

hello world!!!

Press any key to continue

現在我們來分析下上面的代碼,我們采用上面的定義形式實現了如下兩行代碼:

typedef void (*pfun) ();

pfun signal_call(int a,pfun fdsa);

在接下來的main()函數中我們定義了一個函數指針p,使其指向了 func()函數,接下來我們使用了一句 signal_call(1,p)();代碼,實現了func函數調用,那么這到底是怎么實現的呢?那么我們來分析下,前面的signal_call(1,p)返回的是一個函數指針,在代碼中我們發(fā)現其實返回的就是p,所以signal_call(1,p)();就可以變形為p(),看到這種形式我們這就可以很清楚的看出,它調用的就是我們代碼中的func()函數了?,F在讀者明白了signal()函數的實現方法,接下來我們來看看一段使用signal捕捉除數為0時候的異常代碼。

cpp] view plaincopy

#include

#include

#include

#include

#include

#include

jmp_buf buf;

int err;

void handler( int num )

{

err = num;

printf( "發(fā)生浮點計算異常\n");

longjmp( buf, 1);

}

int main( void )

{

double a, b;

char str[20];

int ret;

_control87( 0, _MCW_EM );

if( signal( SIGFPE, handler ) == SIG_ERR )

{

printf("綁定失敗\n" );

abort();

}

ret = setjmp( buf );

if(0 == ret )

{

printf("請輸入被除數:");

scanf("%lf",&a);

printf("請輸入除數:");

scanf("%lf",&b);

printf( "a / b = %4.3g\n", a/b);

printf("發(fā)生異常時候不會被執(zhí)行的語句\n");

}

return 0;

}

沒有發(fā)生異常時候的運行結果:

[cpp] view plaincopy

請輸入被除數:123

請輸入除數:3

a / b = 41

發(fā)生異常時候不會被執(zhí)行的語句

Press any key to continue

發(fā)生異常時候的運行結果:

[cpp] view plaincopy

請輸入被除數:12

請輸入除數:0

發(fā)生浮點計算異常

Press any key to continue

現在來分析下上面的運行結果,先看看_control87( 0, _MCW_EM );這句,可能很多讀者對于這代碼比較陌生,它的功能是開啟所有的浮點計算異常,通常情況下浮點計算異常是被屏蔽掉的,我們?yōu)榱四軌蚴沟媒酉聛淼膕ignal能夠捕捉到浮點計算異常,所以要將其開啟。在往下看我們通過signal( SIGFPE, handler )來綁定了一個浮點計算異常處理函數,如果發(fā)生異常時,那么就調用handler()函數來處理。接下來通過ret = setjmp( buf );保存程序運行的環(huán)境信息,以便接下來的調用longjmp()函數能夠根據這個保存的信息返回該程序先前setjmp()函數的執(zhí)行點。同時我們對比兩次運行的結果發(fā)現如果發(fā)現異常的時候接下來的打印語句“printf("發(fā)生異常時候不會被執(zhí)行的語句\n");”是不會被執(zhí)行的,直接跳轉到我們綁定的handler()函數執(zhí)行了,當然我們在此僅僅是例舉一些簡單的代碼教會讀者學會使用setjmp()函數和longjmp()函數來實現異常處理,讀者完全可以在此基礎上編寫出復雜的異常處理。


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • C語言
    +關注

    關注

    183

    文章

    7644

    瀏覽量

    145558
  • 異常
    +關注

    關注

    0

    文章

    23

    瀏覽量

    9514

原文標題:嵌入式C小秘密之你不知道的異常處理

文章出處:【微信號:edn-china,微信公眾號:EDN電子技術設計】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    C語言安全編碼指南:MISRA C、CERT C、CWE 與 C Secure 標準對比與Perforce QAC應用詳解

    如何編寫真正安全的C語言代碼?指南涵蓋MISRA C、CERT、CWE等國際安全編碼標準對比,以及如何借助Perforce QAC自動檢測漏洞。推薦給嵌入式/汽車/工業(yè)軟件的開發(fā)者!
    的頭像 發(fā)表于 01-26 17:38 ?793次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>語言</b>安全編碼指南:MISRA <b class='flag-5'>C</b>、CERT <b class='flag-5'>C</b>、CWE 與 <b class='flag-5'>C</b> Secure 標準對比與Perforce QAC應用詳解

    講解C語言代碼的實現過程

    重點講解C語言代碼的實現過程,算法的C語言實現過程具有一般性,通過PID算法的C
    發(fā)表于 01-21 07:58

    c語言中的代碼優(yōu)化

    。不少高級語言,包括C++,并不對產生的浮點表達式從新排序,由于那是一個至關復雜的過程。須要注意的是,重排序的代碼和原來的代碼代碼上一致并
    發(fā)表于 01-12 09:45

    C語言主要特點

    ,源程序短,因此輸入程序時工作量少。 2.運算符豐富,C語言的運算符包含的范圍很廣泛,共有34種運算符。C語言把括號、賦值和強制類型轉換等都作為運算符
    發(fā)表于 01-05 07:41

    請問C語言中整形溢出會產生哪些異常行為?

    C語言中整形溢出會產生哪些異常行為?
    發(fā)表于 12-26 07:05

    使用setjmp及l(fā)ongjmp函數處理異常

    使用setjmp和longjmp函數:這是一種用于實現非局部跳轉的方法,就是在程序中設置一個跳轉點,并在某些情況下跳轉到該跳轉點,從而繞過中間的一些代碼或函數。這樣可以在某些情況下模擬異常處理的效果
    發(fā)表于 12-11 08:00

    C語言C++之間的區(qū)別是什么

    處理的問題時拋出異常,并在其他地方捕獲和處理這些異常C語言沒有內建的
    發(fā)表于 12-11 06:23

    C++程序異常處理機制

    運行代碼進行分離,使得程序更加模塊化;另一方面,C++的異常處理可以不需要異常處理
    發(fā)表于 12-02 07:12

    C語言的編程技巧

    一個成員是一個未知大小的數組,適用于動態(tài)分配內存并關聯(lián)一個可變長度的數組。? ?3、匿名結構體和聯(lián)合體?:C語言允許在結構體或聯(lián)合體中定義不帶標簽的內部結構體或聯(lián)合體,簡化代碼結構。 ?4
    發(fā)表于 11-27 06:46

    C語言的分支結構介紹

    1.簡單if語句 C語言中的分支結構語句中的if條件語句。 簡單if語句的基本結構如下: 代碼語言:javascript if(表達式) { 執(zhí)行
    發(fā)表于 11-25 07:48

    C語言特性

    的執(zhí)行效率和資源利用率有著嚴苛的要求。C 語言生成的代碼簡潔緊湊,能夠在有限的硬件條件下快速運行,滿足嵌入式系統(tǒng)對性能的高要求。例如,在智能家居設備的控制芯片中,C
    發(fā)表于 11-24 07:01

    線路保護光纖通道異常處理方法

    通道異常的 常見原因、處理步驟及預防措施 ,幫助運維人員快速定位問題,提升故障處理效率。 廣州郵科光纖線路保護系統(tǒng) 一、光纖通道異常的常見表現 當線路保護光纖通道出現
    的頭像 發(fā)表于 11-17 10:01 ?1096次閱讀
    線路保護光纖通道<b class='flag-5'>異常</b><b class='flag-5'>處理</b>方法

    C語言和單片機C語言有什么差異

    的目標代碼短、運行速度高、存儲空間小、符合C語言的ANSI標準,生成的代碼遵循Intel目標文件格式,而且可與A51匯編語言PL/M51
    發(fā)表于 11-14 07:55

    深入理解C語言C語言循環(huán)控制

    C語言編程中,循環(huán)結構是至關重要的,它可以讓程序重復執(zhí)行特定的代碼塊,從而提高編程效率。然而,為了避免程序進入無限循環(huán),C語言提供了多種循
    的頭像 發(fā)表于 04-29 18:49 ?2034次閱讀
    深入理解<b class='flag-5'>C</b><b class='flag-5'>語言</b>:<b class='flag-5'>C</b><b class='flag-5'>語言</b>循環(huán)控制

    必看!15個C語言常見陷阱及避坑指南

    ? C語言雖強大,但隱藏的“坑”也不少!稍不留神就會導致程序崩潰、數據異常。本文整理15個高頻陷阱,助你寫出更穩(wěn)健的代碼! ? 陷阱1:運算符優(yōu)先級混淆? 問題:運算符優(yōu)先級不同可能導
    的頭像 發(fā)表于 03-16 12:10 ?1776次閱讀