色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

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

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

goto和longjmp函數(shù)的使用

CHANBAEK ? 來(lái)源:嵌入式大本營(yíng) ? 作者:小小飛飛哥 ? 2023-05-23 15:50 ? 次閱讀

分享一下在C程序設(shè)計(jì)當(dāng)中對(duì)異常的處理。主要是介紹一下goto和longjmp函數(shù)的使用。

在寫(xiě)程序的時(shí)候,有些地方很容易出錯(cuò),當(dāng)然這種出錯(cuò)不是說(shuō)那種你寫(xiě)錯(cuò)了,而是說(shuō)比如硬件的初始化失敗了,或者資源暫時(shí)不可用等等導(dǎo)致函數(shù)返回異常。這種錯(cuò)是難以避免的,而且通常是非致命的,只要多嘗試幾次可能就可以了。比如之前我們寫(xiě)過(guò)網(wǎng)絡(luò)編程,要建立網(wǎng)絡(luò)通信,我們需要調(diào)用socket,bind,listen等等一系列函數(shù),每個(gè)函數(shù)都有可能會(huì)出錯(cuò)。

但是你的程序怎么知道該怎么處理呢?程序出錯(cuò)了顯然是不能繼續(xù)往下執(zhí)行的,但是立即終止也不合適,因?yàn)檫@種錯(cuò)是非致命的,那么我們應(yīng)該怎么去設(shè)計(jì)一個(gè)比較健壯的程序呢?今天介紹的可以當(dāng)做是一種思路。

一、使用goto

說(shuō)到goto,可能很多人的第一反應(yīng)是不要用,但是問(wèn)他為什么他可能講不出來(lái),因?yàn)槭莿e人告訴他的。goto真的不能用嗎?當(dāng)然不是,最有力的證明就是Linux內(nèi)核里面就有大量的goto語(yǔ)句。實(shí)際上,只要用的適當(dāng),還是非常好用的,當(dāng)然我并不是說(shuō)程序里面goto滿(mǎn)天飛。

下面舉例說(shuō)明goto的應(yīng)用場(chǎng)景:

有時(shí)候我們完成一件事情要分為很多個(gè)步驟,每個(gè)步驟里面還可能占用一些資源,然而這些步驟很容易出錯(cuò),如果其中某個(gè)步驟出錯(cuò)了,就不能繼續(xù)下一個(gè)步驟,也不能立即終止程序,因?yàn)檫@樣會(huì)使資源得不到釋放。那么使用goto就可以調(diào)出程序并且對(duì)資源進(jìn)行回收。

來(lái)看一段代碼:

#include 
#include 
char *p1=NULL,*p2=NULL,*p3=NULL;


int step1(void);
int step2(void);
int step3(void);


int main(int argc,char* argv[])
{
  if(step1()<0)
  {
    goto error1;
  }
  if(step2()<0)
  {
    goto error2;
  }
  if(step3()<0)
  {
    goto error3;
  }
error3:
  printf("釋放步驟3的資源\\n");
  free(p3);
error2:
  printf("釋放步驟2的資源\\n");
  free(p2);
error1:
  printf("釋放步驟1的資源\\n");
  free(p1);
  exit(0);
}




int step1(void)
{
  p1=(char*)malloc(10);
  return 0;
}


int step2(void)
{
  p2=(char*)malloc(10);
  return 0;
}


int step3(void)
{
  p3=(char*)malloc(10);
  return -1;
}

在這段代碼里面,假設(shè)完成一件事情一共有三個(gè)步驟,每個(gè)步驟里面都維護(hù)了一個(gè)指針變量(資源),假設(shè)步驟一和步驟二都是正常的,步驟三出了問(wèn)題,返回一個(gè)錯(cuò)誤的值,如果我們接收到步驟三的錯(cuò)誤返回值之后立即終止程序,那么步驟一和步驟二里申請(qǐng)的資源就得不到釋放,比如這里的指針會(huì)造成內(nèi)存泄漏,顯然不是我們希望看到的。

但是使用上面的這種結(jié)構(gòu),如果在步驟二出錯(cuò)了,它會(huì)跳轉(zhuǎn)到error2這里先釋放步驟2申請(qǐng)的資源,再釋放步驟一 的資源,最后退出,其他的地方出錯(cuò)也是類(lèi)似處理。上面是一種代碼框架,實(shí)際寫(xiě)代碼應(yīng)該根據(jù)實(shí)際情況來(lái)處理異常。

我們來(lái)看一下效果:

圖片

以上就是goto在多個(gè)步驟容易出錯(cuò)時(shí)的一種處理。這里順便提一下goto的另外一種應(yīng)用場(chǎng)景,就是用來(lái)跳出多層循環(huán)。我們知道跳出循環(huán)一般使用break和continue,但是這個(gè)只能調(diào)出當(dāng)前循環(huán),不能跳出多層循環(huán),有時(shí)候在多層循環(huán)里面,一旦條件滿(mǎn)足,我們就不需要再執(zhí)行后面的循環(huán)了,使用goto可以解決這個(gè)問(wèn)題。

我們來(lái)看一下代碼:

#include 


int main(void)
{
  for(int i=0;i<2;i++)
  {
    for(int j=0;j<2;j++)
    {
      for(int k=0;k<2;k++)
      {
        if(k==1)
        {
          goto lable;
        }
lable2:        printf("i=%d,j=%d,k=%d\\n",i,j,k);
      }

    }
  }
lable:
  printf("after goto \\n");
//  goto lable2;
}

在這里有三層循環(huán)嵌套,一旦條件滿(mǎn)足,就通過(guò)goto跳出整個(gè)循環(huán)體,執(zhí)行后面的代碼。如果使用break ,就非常麻煩。

代碼的執(zhí)行結(jié)果是:

圖片

第一次k=0,正常打印,第二次,k=1,滿(mǎn)足條件,跳出循環(huán),執(zhí)行后面的語(yǔ)句,打印出after goto.

當(dāng)然,問(wèn)題也快出來(lái)了,剛剛是上面跳到了下面,如果我們?cè)購(gòu)南旅嫣先?huì)怎么樣?我們打開(kāi)最后一行的注釋?zhuān)匦戮幾g執(zhí)行,會(huì)發(fā)現(xiàn)打印出幾百上千行的內(nèi)容:

圖片

代碼看起來(lái)好像不復(fù)雜,就是先跳下去,然后又跳回原來(lái)的后面,怎么會(huì)打印這么多東西呢?這就是使用goto不當(dāng)帶來(lái)的害處。這種交叉式地跳來(lái)跳去會(huì)使得程序結(jié)構(gòu)非常混亂,混亂到我也懶得去分析。

二、使用longjmp

剛剛講了goto的異常處理,但是goto有一個(gè)局限性,就是goto只能在一個(gè)函數(shù)內(nèi)進(jìn)行跳轉(zhuǎn),不能跨越函數(shù)。

如果一個(gè)函數(shù)里嵌套了多個(gè)函數(shù)調(diào)用,而里層的函數(shù)出了錯(cuò),希望跳轉(zhuǎn)到上一層或上幾層的函數(shù),該怎么辦?顯然,goto是做不到的。這時(shí)可以使用longjmp函數(shù)。longjmp函數(shù)和setjmp函數(shù)配合使用。

int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

先在程序容易出錯(cuò)的地方使用setjmp,定義一個(gè)入口,等到后面代碼真的出錯(cuò)之后使用longjmp跳轉(zhuǎn)到setjmp處。setjmp直接調(diào)用返回0,若從longjmp返回,則為非0.

舉個(gè)例子:

#include 
#include 


jmp_buf jmpbuffer;


int fun1(void);
int fun2(void);
int fun3(void);


int main(int argc,char* argv[])
{
  printf("這里是主函數(shù)\\n");
  if(setjmp(jmpbuffer)!=0)
  {
    printf("Error\\n");
  }
  fun1();          //假設(shè)fun1是一個(gè)容易出錯(cuò)的函數(shù),出錯(cuò)后將返回上一步,然后再重新執(zhí)行。
  printf("這里是主函數(shù)調(diào)用fun1之后\\n");
  return 0;
}


int fun1(void)
{
  printf("這里是fun1\\n");
  fun2();
}


int fun2(void)
{
  printf("這里是fun2\\n");
  fun3();
}


int fun3(void)
{
  static int i=0;
  printf("這里是fun3\\n");
  if(i++==0)
  {
    longjmp(jmpbuffer,1);   //跳轉(zhuǎn)回main函數(shù)
  }
  return -1;
}

在這里,主函數(shù)調(diào)用了fun1函數(shù),而fun1調(diào)用fun2,fun2又調(diào)用fun3.這種多層嵌套里面,每一層都可能出錯(cuò)。如果我們希望里面任何一層出錯(cuò)了,就返回main函數(shù),那么用longjmp就可以實(shí)現(xiàn)。對(duì)上面程序進(jìn)行解釋?zhuān)?/p>

當(dāng)?shù)谝淮螆?zhí)行setjmp時(shí),由于是直接調(diào)用,所以返回0,接著調(diào)用我們的功能函數(shù)fun1,假設(shè)fun3里面出錯(cuò)了,那么就會(huì)通過(guò)longjmp跳轉(zhuǎn)到setjmp處,同時(shí)攜帶一個(gè)返回值1,那么這時(shí)就會(huì)執(zhí)行if語(yǔ)句進(jìn)行錯(cuò)誤處理,接著再執(zhí)行fun1,也許此時(shí)就全部正常了,一直執(zhí)行到最后。(這是很正常的現(xiàn)象,正如開(kāi)頭說(shuō)的,像硬件初始化,申請(qǐng)資源等都可能不是一次成功的,需要重復(fù)多次)。

而且在多個(gè)地方都可以使用longjmp,攜帶不同的返回值,這樣根據(jù)setjmp的返回值也很容易確定問(wèn)題出在哪里。

來(lái)看一下效果:

圖片

使用longjmp還有一個(gè)問(wèn)題我們可能也需要關(guān)注一下,就是當(dāng)使用longjmp返回的時(shí)候,函數(shù)里的那些變量還能保持原來(lái)的值嗎?我們可以做一個(gè)實(shí)驗(yàn)來(lái)驗(yàn)證這一點(diǎn):

#include 
#include 
#include 


static void f1(int,int,int,int);
static void f2(void);


static jmp_buf jmpbuffer;
static int global;


int main(int argc,char* argv[])
{
  int autoval;
  register int regival;
  volatile int volaval;
  static int staval;
  global=1;autoval=2;regival=3;volaval=4;staval=5;
  if(setjmp(jmpbuffer)!=0)
  {
    printf("after longjmp:\\n");
    printf("global=%d,autoval=%d,regival=%d,volaval=%d,staval=%d\\n", \\
      global,autoval,regival,volaval,staval);
    exit(0);
  }
  global=10;autoval=20;regival=30;volaval=40;staval=50;
  f1(autoval,regival,volaval,staval);
  exit(0);
}


static void f1(int a,int b,int c,int d)
{
  printf("in f1():\\n");
  printf("global=%d,autoval=%d,regival=%d,volaval=%d,staval=%d\\n", \\
    global,a,b,c,d);
  f2();
}


static void f2(void)
{
  longjmp(jmpbuffer,1);
}

這里我們定義了很多種不同的變量,先對(duì)變量賦一個(gè)初值,然后改變變量的值,接著調(diào)用f1,在f1里打印各變量的值,f1再調(diào)用f2,f2使用longjmp跳轉(zhuǎn)回main函數(shù),那么這時(shí)各變量的值如何?是剛開(kāi)始賦的初值,還是后面改變后的值呢?

我們編譯執(zhí)行一下:

圖片

可以發(fā)現(xiàn)使用register聲明的變量保持的是初值,而其他變量都是改變后的值。

如果編譯時(shí)進(jìn)行優(yōu)化,結(jié)果又如何?

圖片

可以發(fā)現(xiàn)除了剛剛的register聲明的變量,普通局部變量(自動(dòng)變量)也沒(méi)有更新,而是保持了初值,這通常不是我們希望的,我們肯定是希望得到最新的值,這也是因?yàn)榫幾g優(yōu)化帶來(lái)的問(wèn)題。所以如果希望避免這個(gè)問(wèn)題,可以加上volatile來(lái)修飾。

以上就是今天要分享的內(nèi)容,主要是在C程序中,由多個(gè)步驟可能引發(fā)的錯(cuò)誤,或者是多層嵌套里面可能出現(xiàn)的錯(cuò)誤進(jìn)行處理,還要注意資源的回收等問(wèn)題。附帶講了亂用goto帶來(lái)的弊端,以及在函數(shù)間跳轉(zhuǎn)與返回時(shí)變量的值的改變,程序優(yōu)化帶來(lái)的影響等。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4338

    瀏覽量

    62743
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4802

    瀏覽量

    68741
  • 程序設(shè)計(jì)
    +關(guān)注

    關(guān)注

    3

    文章

    261

    瀏覽量

    30412
  • C程序
    +關(guān)注

    關(guān)注

    4

    文章

    255

    瀏覽量

    36063
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    嵌入式C語(yǔ)言中的goto語(yǔ)句詳解

    goto語(yǔ)句被稱(chēng)為C語(yǔ)言中的跳轉(zhuǎn)語(yǔ)句。用于無(wú)條件跳轉(zhuǎn)到其他標(biāo)簽。它將控制權(quán)轉(zhuǎn)移到程序的其他部分。
    發(fā)表于 07-19 16:08 ?3408次閱讀
    嵌入式C語(yǔ)言中的<b class='flag-5'>goto</b>語(yǔ)句詳解

    MDK中不能使用Goto Definition Goto Reference的解決方法

    變量定義的類(lèi)型及定義的位置,我們僅需選中該變量然后右擊鼠標(biāo),在彈出的菜單中選中Goto Definition即可自動(dòng)打開(kāi)定義該變量的源文件及其所在位置;如果我們需要查找某個(gè)函數(shù)申明的原型,我們僅需要選中
    發(fā)表于 08-02 10:11

    MDK中不能使用Goto Definition Goto Reference的解決方法

    Definition即可自動(dòng)打開(kāi)定義該變量的源文件及其所在位置。www.11hero.com如果我們需要查找某個(gè)函數(shù)申明的原型,我們僅需要選中該函數(shù)名然后右擊鼠標(biāo),在彈出的菜單中選中Goto
    發(fā)表于 12-11 21:38

    關(guān)于linux中非局部跳轉(zhuǎn)的簡(jiǎn)單使用

    也是有局限性的,那就是goto語(yǔ)句是一個(gè)實(shí)現(xiàn)局部跳轉(zhuǎn)的關(guān)鍵字,也就是只能在一個(gè)函數(shù)中進(jìn)行跳轉(zhuǎn),它是無(wú)法在不同的函數(shù)中實(shí)現(xiàn)跳轉(zhuǎn)的。那么如何實(shí)現(xiàn)在不同的函數(shù)中進(jìn)行非局部的跳轉(zhuǎn)呢?而setj
    發(fā)表于 10-24 16:42

    關(guān)于Linux中非局部跳轉(zhuǎn)的簡(jiǎn)單使用

    代碼邏輯容易混亂,大大降低了代碼的可讀性和可維護(hù)性。而且即使使用goto語(yǔ)句進(jìn)行跳轉(zhuǎn)也是有局限性的,那就是goto語(yǔ)句是一個(gè)實(shí)現(xiàn)局部跳轉(zhuǎn)的關(guān)鍵字,也就是只能在一個(gè)函數(shù)中進(jìn)行跳轉(zhuǎn),它是無(wú)法在不同的
    發(fā)表于 05-16 11:40

    如何實(shí)現(xiàn)setjmp和longjmp的跳轉(zhuǎn)

    想實(shí)現(xiàn)這么一個(gè)功能,我在定時(shí)中斷中判斷一個(gè)輸入點(diǎn)P1.0,當(dāng)P1.0接收到高電平時(shí)進(jìn)入跳轉(zhuǎn),轉(zhuǎn)到一個(gè)函數(shù)。下面代碼void Timer1_Interrupt()interrupt 3{ TH1=xx
    發(fā)表于 07-22 01:32

    【微信精選】為什么在C語(yǔ)言中,goto這么不受待見(jiàn)?

    控制。該類(lèi)語(yǔ)句允許程序員對(duì)當(dāng)前代碼行斷行,而直接進(jìn)入另一個(gè)不同的代碼段。列表1為簡(jiǎn)單的示例。編程語(yǔ)言終究開(kāi)始引入了函數(shù)的概念,即允許程序?qū)Υa進(jìn)行斷行。如果已經(jīng)完成,不再使用goto語(yǔ)句來(lái)表示代碼
    發(fā)表于 09-11 07:30

    setjmp、longjmp原理探究及實(shí)現(xiàn)

    setjmp、longjmp原理探究及實(shí)現(xiàn)一、原理1、實(shí)驗(yàn)2、猜想二、實(shí)現(xiàn)三、調(diào)用四、總結(jié)一、原理C語(yǔ)言中包含頭文件即可實(shí)現(xiàn)跨函數(shù)跳轉(zhuǎn),通常用于異常處理,在運(yùn)行代碼出現(xiàn)異常時(shí)可以自動(dòng)跳轉(zhuǎn)到調(diào)用
    發(fā)表于 01-25 07:08

    淺談C語(yǔ)言goto語(yǔ)句的用法

    冒號(hào)(:)。語(yǔ)句標(biāo)號(hào)起標(biāo)識(shí)語(yǔ)句的作用,與goto 語(yǔ)句配合使用。執(zhí)行goto語(yǔ)句后,程序?qū)⑻D(zhuǎn)到該標(biāo)號(hào)處并執(zhí)行其后的語(yǔ)句。另外標(biāo)號(hào)必須與goto語(yǔ)句同處于一個(gè)函數(shù)中,但可以不在一個(gè)循環(huán)
    發(fā)表于 05-06 09:16

    Proteus之goto語(yǔ)句的應(yīng)用

    Proteus之goto語(yǔ)句的應(yīng)用,很好的Proteus資料,快來(lái)學(xué)習(xí)吧。
    發(fā)表于 04-18 14:49 ?0次下載

    C語(yǔ)言中的goto語(yǔ)句怎么用?為什么反對(duì)使用

    關(guān)于C語(yǔ)言的goto語(yǔ)句存在很多爭(zhēng)議,很多書(shū)籍都建議“謹(jǐn)慎使用,或者根本不用”。這里先不做過(guò)多的討論,存在即合理,既然是C語(yǔ)言中的一個(gè)知識(shí)點(diǎn),我們還是有必要學(xué)會(huì)使用。先看一些goto如何用:
    的頭像 發(fā)表于 01-24 17:38 ?6680次閱讀
    C語(yǔ)言中的<b class='flag-5'>goto</b>語(yǔ)句怎么用?為什么反對(duì)使用

    C語(yǔ)言中到底應(yīng)不應(yīng)該使用goto語(yǔ)句

    關(guān)于C語(yǔ)言的goto語(yǔ)句存在很多爭(zhēng)議,很多書(shū)籍都建議“謹(jǐn)慎使用,或者根本不用”。這里先不做過(guò)多的討論,存在即合理,既然是C語(yǔ)言中的一個(gè)知識(shí)點(diǎn),我們還是有必要學(xué)會(huì)使用。先看一些goto如何用:
    的頭像 發(fā)表于 01-16 09:12 ?6807次閱讀

    goto的優(yōu)缺點(diǎn)

    很多書(shū)籍都會(huì)把goto當(dāng)成反面教材使用,認(rèn)為如果使用不當(dāng),將會(huì)造成很多意想不到的問(wèn)題。但goto作為C語(yǔ)言的一部分,存在即合理,goto有它的缺點(diǎn),也有它的優(yōu)點(diǎn)。 缺點(diǎn): 很容易把邏輯弄亂,增加理解
    的頭像 發(fā)表于 09-29 14:27 ?4120次閱讀

    Cortex-M單片機(jī)中 setjmp、longjmp原理探究及實(shí)現(xiàn)

    setjmp、longjmp原理探究及實(shí)現(xiàn)一、原理1、實(shí)驗(yàn)2、猜想二、實(shí)現(xiàn)三、調(diào)用四、總結(jié)一、原理C語(yǔ)言中包含頭文件 <setjump.h> 即可實(shí)現(xiàn)跨函數(shù)跳轉(zhuǎn),通常
    發(fā)表于 12-01 11:36 ?15次下載
    Cortex-M單片機(jī)中 setjmp、<b class='flag-5'>longjmp</b>原理探究及實(shí)現(xiàn)

    深入探討嵌入式C編程的goto語(yǔ)句

    什么是goto語(yǔ)句? goto 語(yǔ)句被稱(chēng)為 C 語(yǔ)言中的跳轉(zhuǎn)語(yǔ)句。 用于無(wú)條件跳轉(zhuǎn)到其他標(biāo)簽。它將控制權(quán)轉(zhuǎn)移到程序的其他部分。 goto 語(yǔ)句一般很少使用,因?yàn)樗钩绦虻目勺x性和復(fù)雜性變得更差。
    發(fā)表于 01-21 10:41 ?623次閱讀
    深入探討嵌入式C編程的<b class='flag-5'>goto</b>語(yǔ)句
    主站蜘蛛池模板: 久久精品在现线观看免费15| 99re久久超碰视频精品| 伊人久久大香线蕉无码麻豆| www色小姐| 久久99免费视频| 三级黄色在线看| 131美女爱做视频午夜剧场| 国产乱对白精彩在线播放 | 里番acg纲手的熟蜜姬训练场| 色中色辩论区| 99香蕉视频| 久久久久999| 小向美奈子厨房magnet| yellow片在线观看免费观看动漫| 久久九九日本韩国精品| 性感尼姑风流寺| 俄罗斯美幼| 欧美18videosex初次| 一线高清视频在线播放| 国产欧美亚洲综合第一页| 人妖操女人| 99精品国产免费久久久久久下载 | 青青草原在线新免费| 2020国产成人精品免费视频| 国产亚洲精品线观看不卡| 日韩在线看片中文字幕不卡| 99精品网站| 久久内在线视频精品mp4| 亚洲精品国产在线网站| 国产高清视频a在线大全| 欧美人与禽ZOZO性伦交视频| 18禁国产精品久久久久久麻豆| 极品美女穴| 亚洲va精品中文字幕| 国产成人永久免费视频| 日本不卡一二三| a亚洲在线观看不卡高清| 美女漏bb| 中文字幕无码亚洲字幕成A人蜜桃| 黄片在线观看| 亚洲精品国产拍在线观看|