嵌入式C之錯(cuò)誤處理

嵌入式大雜燴 2023-12-22 20:26

星標(biāo)公眾號(hào)」,一起進(jìn)步!

來(lái)源:https://www.cnblogs.com/clover-toeic/p/3919857.html

前言

本文主要總結(jié)嵌入式系統(tǒng)C語(yǔ)言編程中,主要的錯(cuò)誤處理方式。文中涉及的代碼運(yùn)行環(huán)境如下:

一、錯(cuò)誤概念

1.1 錯(cuò)誤分類

從嚴(yán)重性而言,程序錯(cuò)誤可分為致命性和非致命性兩類。對(duì)于致命性錯(cuò)誤,無(wú)法執(zhí)行恢復(fù)動(dòng)作,最多只能在用戶屏幕上打印出錯(cuò)消息或?qū)⑵鋵?xiě)入日志文件,然后終止程序;而對(duì)于非致命性錯(cuò)誤,多數(shù)本質(zhì)上是暫時(shí)的(如資源短缺),一般恢復(fù)動(dòng)作是延遲一些時(shí)間后再次嘗試。

從交互性而言,程序錯(cuò)誤可分為用戶錯(cuò)誤和內(nèi)部錯(cuò)誤兩類。用戶錯(cuò)誤呈現(xiàn)給用戶,通常指明用戶操作上的錯(cuò)誤;而程序內(nèi)部錯(cuò)誤呈現(xiàn)給程序員(可能攜帶用戶不可接觸的數(shù)據(jù)細(xì)節(jié)),用于查錯(cuò)和排障。

應(yīng)用程序開(kāi)發(fā)者可決定恢復(fù)哪些錯(cuò)誤以及如何恢復(fù)。例如,若磁盤(pán)已滿,可考慮刪除非必需或已過(guò)期的數(shù)據(jù);若網(wǎng)絡(luò)連接失敗,可考慮短時(shí)間延遲后重建連接。選擇合理的錯(cuò)誤恢復(fù)策略,可避免應(yīng)用程序的異常終止,從而改善其健壯性。

1.2 處理步驟

錯(cuò)誤處理即處理程序運(yùn)行時(shí)出現(xiàn)的任何意外或異常情況。典型的錯(cuò)誤處理包含五個(gè)步驟:

  1. 程序執(zhí)行時(shí)發(fā)生軟件錯(cuò)誤。該錯(cuò)誤可能產(chǎn)生于被底層驅(qū)動(dòng)或內(nèi)核映射為軟件錯(cuò)誤的硬件響應(yīng)事件(如除零)。

  2. 以一個(gè)錯(cuò)誤指示符(如整數(shù)或結(jié)構(gòu)體)記錄錯(cuò)誤的原因及相關(guān)信息。

  3. 程序檢測(cè)該錯(cuò)誤(讀取錯(cuò)誤指示符,或由其主動(dòng)上報(bào));

  4. 程序決定如何處理錯(cuò)誤(忽略、部分處理或完全處理);

  5. 恢復(fù)或終止程序的執(zhí)行。

上述步驟用C語(yǔ)言代碼表述如下:

int?func()
{
????int?bIsErrOccur?=?0;
????//do?something?that?might?invoke?errors
????if(bIsErrOccur)??//Stage?1:?error?occurred
????????return?-1;???//Stage?2:?generate?error?indicator
????//...
????return?0;
}

int?main(void)
{
????if(func()?!=?0)??//Stage?3:?detect?error
????{
????????//Stage?4:?handle?error
????}
????//Stage?5:?recover?or?abort
????return?0;
}

調(diào)用者可能希望函數(shù)返回成功時(shí)表示完全成功,失敗時(shí)程序恢復(fù)到調(diào)用前的狀態(tài)(但被調(diào)函數(shù)很難保證這點(diǎn))。

二 、錯(cuò)誤傳遞

2.1 返回值和回傳參數(shù)

C語(yǔ)言通常使用返回值來(lái)標(biāo)志函數(shù)是否執(zhí)行成功,調(diào)用者通過(guò)if等語(yǔ)句檢查該返回值以判斷函數(shù)執(zhí)行情況。常見(jiàn)的幾種調(diào)用形式如下:

if((p?=?malloc(100))?==?NULL)
???//...

if((c?=?getchar())?==?EOF)
???//...

if((ticks?=?clock())?0)
???//...

Unix系統(tǒng)調(diào)用級(jí)函數(shù)(和一些老的Posix函數(shù))的返回值有時(shí)既包括錯(cuò)誤代碼也包括有用結(jié)果。因此,上述調(diào)用形式可在同一條語(yǔ)句中接收返回值并檢查錯(cuò)誤(當(dāng)執(zhí)行成功時(shí)返回合法的數(shù)據(jù)值)。

返回值方式的好處是簡(jiǎn)便和高效,但仍存在較多問(wèn)題:

  1. 代碼可讀性降低

沒(méi)有返回值的函數(shù)是不可靠的。但若每個(gè)函數(shù)都具有返回值,為保持程序健壯性,就必須對(duì)每個(gè)函數(shù)進(jìn)行正確性驗(yàn)證,即調(diào)用時(shí)檢查其返回值。這樣,代碼中很大一部分可能花費(fèi)在錯(cuò)誤處理上,且排錯(cuò)代碼和正常流程代碼攪在一起,比較混亂。

  1. 質(zhì)量降級(jí)

條件語(yǔ)句相比其他類型的語(yǔ)句潛藏更多的錯(cuò)誤。不必要的條件語(yǔ)句會(huì)增加排障和白盒測(cè)試的工作量。

  1. 信息有限

通過(guò)返回值只能返回一個(gè)值,因此一般只能簡(jiǎn)單地標(biāo)志成功或失敗,而無(wú)法作為獲知具體錯(cuò)誤信息的手段。通過(guò)按位編碼可變通地返回多個(gè)值,但并不常用。

字符串處理函數(shù)可參考IntToAscii()來(lái)返回具體的錯(cuò)誤原因,并支持鏈?zhǔn)奖磉_(dá):

char?*IntToAscii(int?dwVal,?char?*pszRes,?int?dwRadix)
{
????if(NULL?==?pszRes)
????????return?"Arg2Null";

????if((dwRadix?2)?||?(dwRadix?>?36))
????????return?"Arg3OutOfRange";

????//...
????return?pszRes;
}
  1. 定義沖突

不同函數(shù)在成功和失敗時(shí)返回值的取值規(guī)則可能不同。例如,Unix系統(tǒng)調(diào)用級(jí)函數(shù)返回0代表成功,-1代表失?。恍碌腜osix函數(shù)返回0代表成功,非0代表失敗;標(biāo)準(zhǔn)C庫(kù)中isxxx函數(shù)返回1表示成功,0表示失敗。

  1. 無(wú)約束性

調(diào)用者可以忽略和丟棄返回值。未檢查和處理返回值時(shí),程序仍然能夠運(yùn)行,但結(jié)果不可預(yù)知。

新的Posix函數(shù)返回值只攜帶狀態(tài)和異常信息,并通過(guò)參數(shù)列表中的指針回傳有用的結(jié)果。 回傳參數(shù)綁定到相應(yīng)的實(shí)參上,因此調(diào)用者不可能完全忽略它們。通過(guò)回傳參數(shù)(如結(jié)構(gòu)體指針)可返回多個(gè)值,也可攜帶更多的信息。

綜合返回值和回傳參數(shù)的優(yōu)點(diǎn),可對(duì)Get類函數(shù)采用返回值(含有用結(jié)果)方式,而對(duì)Set類函數(shù)采用返回值+回傳參數(shù)方式。

對(duì)于純粹的返回值,可按需提供如下解析接口:

typedef?enum{
????S_OK,???????????????????//成功
????S_ERROR,????????????????//失敗(原因未明確),通用狀態(tài)

????S_NULL_POINTER,?????????//入?yún)⒅羔槥镹ULL
????S_ILLEGAL_PARAM,????????//參數(shù)值非法,通用
????S_OUT_OF_RANGE,?????????//參數(shù)值越限
????S_MAX_STATUS????????????//不可作為返回值狀態(tài),僅作枚舉最值使用
}FUNC_STATUS;

#define?RC_NAME(eRetCode)?\
????((eRetCode)?==?S_OK????????????????????????"Success"?????????????:?\
????((eRetCode)?==?S_ERROR?????????????????????"Failure"?????????????:?\
????((eRetCode)?==?S_NULL_POINTER??????????????"NullPointer"?????????:?\
????((eRetCode)?==?S_ILLEGAL_PARAM?????????????"IllegalParas"????????:?\
????((eRetCode)?==?S_OUT_OF_RANGE??????????????"OutOfRange"??????????:?\
??????"Unknown")))))

當(dāng)返回值錯(cuò)誤碼來(lái)自下游模塊時(shí),可能與本模塊錯(cuò)誤碼沖突。此時(shí),建議不要將下游錯(cuò)誤碼直接向上傳遞,以免引起混亂。若允許向終端或文件輸出錯(cuò)誤信息,則可詳細(xì)記錄出錯(cuò)現(xiàn)場(chǎng)(如函數(shù)名、錯(cuò)誤描述、參數(shù)取值等),并轉(zhuǎn)換為本模塊定義的錯(cuò)誤碼再向上傳遞。

2.2 全局狀態(tài)標(biāo)志(errno)

Unix系統(tǒng)調(diào)用或某些C標(biāo)準(zhǔn)庫(kù)函數(shù)出錯(cuò)時(shí),通常返回一個(gè)負(fù)值,并設(shè)置全局整型變量errno為一個(gè)含有錯(cuò)誤信息的值。例如,open函數(shù)出錯(cuò)時(shí)返回-1,并設(shè)置errno為EACESS(權(quán)限不足)等值。

C標(biāo)準(zhǔn)庫(kù)頭文件中定義errno及其可能的非零常量取值(以字符'E'開(kāi)頭)。在ANSI C中已定義一些基本的errno常量,操作系統(tǒng)也會(huì)擴(kuò)展一部分(但其對(duì)錯(cuò)誤描述仍顯匱乏)。Linux系統(tǒng)中,出錯(cuò)常量在errno(3)手冊(cè)頁(yè)中列出,可通過(guò)man 3 errno命令查看。除EAGAIN和EWOULDBLOCK取值相同外,POSIX.1指定的所有出錯(cuò)編號(hào)取值均不同。

Posix和ISO C將errno定義為一個(gè)可修改的整型左值(lvalue),可以是包含出錯(cuò)編號(hào)的一個(gè)整數(shù),或是一個(gè)返回出錯(cuò)編號(hào)指針的函數(shù)。以前使用的定義為:

extern?int?errno;

但在多線程環(huán)境中,多個(gè)線程共享進(jìn)程地址空間,每個(gè)線程都有屬于自己的局部errno(thread-local)以避免一個(gè)線程干擾另一個(gè)線程。例如,Linux支持多線程存取errno,將其定義為:

extern?int?*__errno_location(void);
#define?errno?(*__errno_location())

函數(shù)__ errno_location在不同的庫(kù)版本下有不同的定義,在單線程版本中,直接返回全局變量errno的地址;而在多線程版本中,不同線程調(diào)用__errno_location返回的地址則各不相同。

C運(yùn)行庫(kù)中主要在math.h(數(shù)學(xué)運(yùn)算)和stdio.h(I/O操作)頭文件聲明的函數(shù)中使用errno。

使用errno時(shí)應(yīng)注意以下幾點(diǎn):

  1. 函數(shù)返回成功時(shí),允許其修改errno。

例如,調(diào)用fopen函數(shù)新建文件時(shí),內(nèi)部可能會(huì)調(diào)用其他庫(kù)函數(shù)檢測(cè)是否存在同名文件。而用于檢測(cè)文件的庫(kù)函數(shù)在文件不存在時(shí),可能會(huì)失敗并設(shè)置errno。這樣, fopen函數(shù)每次新建一個(gè)事先并不存在的文件時(shí),即使沒(méi)有任何程序錯(cuò)誤發(fā)生(fopen本身成功返回),errno也仍然可能被設(shè)置。

因此,調(diào)用庫(kù)函數(shù)時(shí)應(yīng)先檢測(cè)作為錯(cuò)誤指示的返回值。僅當(dāng)函數(shù)返回值指明出錯(cuò)時(shí),才檢查errno值:

//調(diào)用庫(kù)函數(shù)
if(返回錯(cuò)誤值)
????//檢查errno
  1. 庫(kù)函數(shù)返回失敗時(shí),不一定會(huì)設(shè)置errno,取決于具體的庫(kù)函數(shù)。

  2. errno在程序開(kāi)始時(shí)設(shè)置為0,任何庫(kù)函數(shù)都不會(huì)將errno再次清零。

因此,在調(diào)用可能設(shè)置errno的運(yùn)行庫(kù)函數(shù)之前,最好先將errno設(shè)置為0。調(diào)用失敗后再檢查errno的值。

  1. 使用errno前,應(yīng)避免調(diào)用其他可能設(shè)置errno的庫(kù)函數(shù)。如:
if?(somecall()?==?-1)
{
????printf("somecall()?failed\n");
????if(errno?==?...)?{?...?}
}

somecall()函數(shù)出錯(cuò)返回時(shí)設(shè)置errno。但當(dāng)檢查errno時(shí),其值可能已被printf()函數(shù)改變。

若要正確使用somecall()函數(shù)設(shè)置的errno,須在調(diào)用printf()函數(shù)前保存其值:

if?(somecall()?==?-1)
{
????int?dwErrSaved?=?errno;
????printf("somecall()?failed\n");
????if(dwErrSaved?==?...)?{?...?}
}

類似地,當(dāng)在信號(hào)處理程序中調(diào)用可重入函數(shù)時(shí),應(yīng)在其前保存其后恢復(fù)errno值。

  1. 使用現(xiàn)代版本的C庫(kù)時(shí),應(yīng)包含使用頭文件;在非常老的Unix 系統(tǒng)中,可能沒(méi)有該頭文件,此時(shí)可手工聲明errno(如extern int errno)。

C標(biāo)準(zhǔn)定義strerror和perror兩個(gè)函數(shù),以幫助打印錯(cuò)誤信息。

#include?
char?*strerror(int?errnum);

該函數(shù)將errnum(即errno值)映射為一個(gè)出錯(cuò)信息字符串,并返回指向該字符串的指針??蓪⒊鲥e(cuò)字符串和其它信息組合輸出到用戶界面,或保存到日志文件中,如通過(guò)fprintf(fp, "somecall failed(%s)", strerror(errno))將錯(cuò)誤消息打印到fp指向的文件中。

perror函數(shù)將當(dāng)前errno對(duì)應(yīng)的錯(cuò)誤消息的字符串輸出到標(biāo)準(zhǔn)錯(cuò)誤(即stderr或2)上。

#include?
void?perror(const?char?*msg);

該函數(shù)首先輸出由msg指向的字符串(用戶自己定義的信息),后面緊跟一個(gè)冒號(hào)和空格,然后是當(dāng)前errno值對(duì)應(yīng)的錯(cuò)誤類型描述,最后是一個(gè)換行符。未使用重定向時(shí),該函數(shù)輸出到控制臺(tái)上;若將標(biāo)準(zhǔn)錯(cuò)誤輸出重定向到/dev/null,則看不到任何輸出。

注意,perror()函數(shù)中errno對(duì)應(yīng)的錯(cuò)誤消息集合與strerror()相同。但后者可提供更多定位信息和輸出方式。

兩個(gè)函數(shù)的用法示例如下:

int?main(int?argc,?char**?argv)
{
????errno?=?0;
????FILE?*pFile?=?fopen(argv[1],?"r");
????if(NULL?==?pFile)
????{
????????printf("Cannot?open?file?'%s'(%s)!\n",?argv[1],?strerror(errno));
????????perror("Open?file?failed");
????}
????else
????{
????????printf("Open?file?'%s'(%s)!\n",?argv[1],?strerror(errno));
????????perror("Open?file");
????????fclose(pFile);
????}

????return?0;
}

執(zhí)行結(jié)果為:

[wangxiaoyuan_@localhost?test1]$?./GlbErr?/sdb1/wangxiaoyuan/linux_test/test1/test.c
Open?file?'/sdb1/wangxiaoyuan/linux_test/test1/test.c'(Success)!
Open?file:?Success
[wangxiaoyuan_@localhost?test1]$?./GlbErr?NonexistentFile.h
Cannot?open?file?'NonexistentFile.h'(No?such?file?or?directory)!
Open?file?failed:?No?such?file?or?directory
[wangxiaoyuan_@localhost?test1]$?./GlbErr?NonexistentFile.h?>?test
Open?file?failed:?No?such?file?or?directory
[wangxiaoyuan_@localhost?test1]$?./GlbErr?NonexistentFile.h?2>?test
Cannot?open?file?'NonexistentFile.h'(No?such?file?or?directory)!

也可仿照errno的定義和處理,定制自己的錯(cuò)誤代碼:

int?*_fpErrNo(void)
{
???static?int?dwLocalErrNo?=?0;
???return?&dwLocalErrNo;
}

#define?ErrNo?(*_fpErrNo())
#define?EOUTOFRANGE??1
//define?other?error?macros...

int?Callee(void)
{
????ErrNo?=?1;
????return?-1;
}

int?main(void)
{
????ErrNo?=?0;
????if((-1?==?Callee())?&&?(EOUTOFRANGE?==?ErrNo))
????????printf("Callee?failed(ErrNo:%d)!\n",?ErrNo);
????return?0;
}

借助全局狀態(tài)標(biāo)志,可充分利用函數(shù)的接口(返回值和參數(shù)表)。但與返回值一樣,它隱含地要求調(diào)用者在調(diào)用函數(shù)后檢查該標(biāo)志,而這種約束同樣脆弱。

此外,全局狀態(tài)標(biāo)志存在重用和覆蓋的風(fēng)險(xiǎn)。而函數(shù)返回值是無(wú)名的臨時(shí)變量,由函數(shù)產(chǎn)生且只能被調(diào)用者訪問(wèn)。調(diào)用完成后即可檢查或拷貝返回值,然后原始的返回對(duì)象將消失而不能被重用。又因?yàn)闊o(wú)名,返回值不能被覆蓋。

2.3 局部跳轉(zhuǎn)(goto)

使用goto語(yǔ)句可直接跳轉(zhuǎn)到函數(shù)內(nèi)的錯(cuò)誤處理代碼處。以除零錯(cuò)誤為例:

double?Division(double?fDividend,?double?fDivisor)
{
????return?fDividend/fDivisor;
}
int?main(void)
{
????int?dwFlag?=?0;
????if(1?==?dwFlag)
????{
????RaiseException:
????????printf("The?divisor?cannot?be?0!\n");
????????exit(1);
????}
????dwFlag?=?1;

????double?fDividend?=?0.0,?fDivisor?=?0.0;
????printf("Enter?the?dividend:?");
????scanf("%lf",?&fDividend);
????printf("Enter?the?divisor?:?");
????scanf("%lf",?&fDivisor);
????if(0?==?fDivisor)?//不太嚴(yán)謹(jǐn)?shù)母↑c(diǎn)數(shù)判0比較
????????goto?RaiseException;
????printf("The?quotient?is?%.2lf\n",?Division(fDividend,?fDivisor));

????return?0;
}

執(zhí)行結(jié)果如下:

[wangxiaoyuan_@localhost?test1]$?./test
Enter?the?dividend:?10
Enter?the?divisor?:?0
The?divisor?cannot?be?0!
[wangxiaoyuan_@localhost?test1]$?./test
Enter?the?dividend:?10
Enter?the?divisor?:?2
The?quotient?is?5.00

雖然goto語(yǔ)句會(huì)破壞代碼結(jié)構(gòu)性,但卻非常適用于集中錯(cuò)誤處理。偽代碼示例如下:

CallerFunc()
{
????if((ret?=?CalleeFunc1())?0);
????????goto?ErrHandle;
????if((ret?=?CalleeFunc2())?0);
????????goto?ErrHandle;
????if((ret?=?CalleeFunc3())?0);
????????goto?ErrHandle;
????//...

????return;

ErrHandle:
????//Handle?Error(e.g.?printf)
????return;
}

2.4 非局部跳轉(zhuǎn)(setjmp/longjmp)

局部goto語(yǔ)句只能跳到所在函數(shù)內(nèi)部的標(biāo)號(hào)上。若要跨越函數(shù)跳轉(zhuǎn),需要借助標(biāo)準(zhǔn)C庫(kù)提供非局部跳轉(zhuǎn)函數(shù)setjmp()和longjmp()。它們分別承擔(dān)非局部標(biāo)號(hào)和goto的作用,非常適用于處理發(fā)生在深層嵌套函數(shù)調(diào)用中的出錯(cuò)情況?!胺蔷植刻D(zhuǎn)”是在棧上跳過(guò)若干調(diào)用幀,返回到當(dāng)前函數(shù)調(diào)用路徑上的某個(gè)函數(shù)內(nèi)。

#include?
int?setjmp(jmp_buf?env);
void?longjmp(jmp_buf?env,int?val);

函數(shù)setjmp()將程序運(yùn)行時(shí)的當(dāng)前系統(tǒng)堆棧環(huán)境保存在緩沖區(qū)env結(jié)構(gòu)中。初次調(diào)用該函數(shù)時(shí)返回值為0。longjmp()函數(shù)根據(jù)setjmp()所保存的env結(jié)構(gòu)恢復(fù)先前的堆棧環(huán)境,即“跳回”先前調(diào)用setjmp時(shí)的程序執(zhí)行點(diǎn)。

此時(shí),setjmp()函數(shù)返回longjmp()函數(shù)所設(shè)置的參數(shù)val值,程序?qū)⒗^續(xù)執(zhí)行setjmp調(diào)用后的下一條語(yǔ)句(仿佛從未離開(kāi)setjmp)。參數(shù)val為非0值,若設(shè)置為0,則setjmp()函數(shù)返回1。

可見(jiàn),setjmp()有兩類返回值,用于區(qū)分是首次直接調(diào)用(返回0)和還是由其他地方跳轉(zhuǎn)而來(lái)(返回非0值)。對(duì)于一個(gè)setjmp可有多個(gè)longjmp,因此可由不同的非0返回值區(qū)分這些longjmp。

舉個(gè)簡(jiǎn)單例子說(shuō)明 setjmp/longjmp的非局部跳轉(zhuǎn):

jmp_buf?gJmpBuf;
void?Func1(){
????printf("Enter?Func1\n");
????if(0)longjmp(gJmpBuf,?1);
}
void?Func2(){
????printf("Enter?Func2\n");
????if(0)longjmp(gJmpBuf,?2);
}
void?Func3(){
????printf("Enter?Func3\n");
????if(1)longjmp(gJmpBuf,?3);
}

int?main(void)
{
????int?dwJmpRet?=?setjmp(gJmpBuf);
????printf("dwJmpRet?=?%d\n",?dwJmpRet);
????if(0?==?dwJmpRet)
????{
????????Func1();
????????Func2();
????????Func3();
????}
????else
????{
????????switch(dwJmpRet)
????????{
????????????case?1:
????????????????printf("Jump?back?from?Func1\n");
????????????break;
????????????case?2:
????????????????printf("Jump?back?from?Func2\n");
????????????break;
????????????case?3:
????????????????printf("Jump?back?from?Func3\n");
????????????break;
????????????default:
????????????????printf("Unknown?Func!\n");
????????????break;
????????}
????}
????return?0;
}

執(zhí)行結(jié)果為:

dwJmpRet?=?0
Enter?Func1
Enter?Func2
Enter?Func3
dwJmpRet?=?3
Jump?back?from?Func3

當(dāng)setjmp/longjmp嵌在單個(gè)函數(shù)中使用時(shí),可模擬PASCAL語(yǔ)言中嵌套函數(shù)定義(即函數(shù)內(nèi)中定義一個(gè)局部函數(shù))。當(dāng)setjmp/longjmp跨越函數(shù)使用時(shí),可模擬面向?qū)ο笳Z(yǔ)言中的異常(exception) 機(jī)制。

模擬異常機(jī)制時(shí),首先通過(guò)setjmp()函數(shù)設(shè)置一個(gè)跳轉(zhuǎn)點(diǎn)并保存返回現(xiàn)場(chǎng),然后使用try塊包含那些可能出現(xiàn)錯(cuò)誤的代碼??稍趖ry塊代碼中或其調(diào)用的函數(shù)內(nèi),通過(guò)longjmp()函數(shù)拋出(throw)異常。

拋出異常后,將跳回setjmp()函數(shù)所設(shè)置的跳轉(zhuǎn)點(diǎn)并執(zhí)行catch塊所包含的異常處理程序。

以除零錯(cuò)誤為例:

jmp_buf?gJmpBuf;
void?RaiseException(void)
{
???printf("Exception?is?raised:?");
???longjmp(gJmpBuf,?1);??//throw,跳轉(zhuǎn)至異常處理代碼
???printf("This?line?should?never?get?printed!\n");
}
double?Division(double?fDividend,?double?fDivisor)
{
????return?fDividend/fDivisor;
}
int?main(void)
{
????double?fDividend?=?0.0,?fDivisor?=?0.0;
????printf("Enter?the?dividend:?");
????scanf("%lf",?&fDividend);
????printf("Enter?the?divisor?:?");
????if(0?==?setjmp(gJmpBuf))??//try塊
????{
????????scanf("%lf",?&fDivisor);
????????if(0?==?fDivisor)?//也可將該判斷及RaiseException置于Division內(nèi)
????????????RaiseException();
????????printf("The?quotient?is?%.2lf\n",?Division(fDividend,?fDivisor));
????}
????else??//catch塊(異常處理代碼)
????{
????????printf("The?divisor?cannot?be?0!\n");
????}

????return?0;
}

執(zhí)行結(jié)果為:

Enter?the?dividend:?10
Enter?the?divisor?:?0
Exception?is?raised:?The?divisor?cannot?be?0!

通過(guò)組合使用setjmp/longjmp函數(shù),可對(duì)復(fù)雜程序中可能出現(xiàn)的異常進(jìn)行集中處理。根據(jù)longjmp()函數(shù)所傳遞的返回值來(lái)區(qū)分處理各種不同的異常。

使用setjmp/longjmp函數(shù)時(shí)應(yīng)注意以下幾點(diǎn):

  1. 必須先調(diào)用setjmp()函數(shù)后調(diào)用longjmp()函數(shù),以恢復(fù)到先前被保存的程序執(zhí)行點(diǎn)。若調(diào)用順序相反,將導(dǎo)致程序的執(zhí)行流變得不可預(yù)測(cè),很容易導(dǎo)致程序崩潰。

  2. longjmp()函數(shù)必須在setjmp()函數(shù)的作用域之內(nèi)。在調(diào)用setjmp()函數(shù)時(shí),它保存的程序執(zhí)行點(diǎn)環(huán)境只在當(dāng)前主調(diào)函數(shù)作用域以內(nèi)(或以后)有效。若主調(diào)函數(shù)返回或退出到上層(或更上層)的函數(shù)環(huán)境中,則setjmp()函數(shù)所保存的程序環(huán)境也隨之失效(函數(shù)返回時(shí)堆棧內(nèi)存失效)。這就要求setjmp()不可該封裝在一個(gè)函數(shù)中,若要封裝則必須使用宏(詳見(jiàn)《C語(yǔ)言接口與實(shí)現(xiàn)》“第4章 異常與斷言”)。

  3. 通常將jmp_buf變量定義為全局變量,以便跨函數(shù)調(diào)用longjmp。

  4. 通常,存放在存儲(chǔ)器中的變量將具有l(wèi)ongjmp時(shí)的值,而在CPU和浮點(diǎn)寄存器中的變量則恢復(fù)為調(diào)用setjmp時(shí)的值。因此,若在調(diào)用setjmp和longjmp之間修改自動(dòng)變量或寄存器變量的值,當(dāng)setjmp從longjmp調(diào)用返回時(shí),變量將維持修改后的值。若要編寫(xiě)使用非局部跳轉(zhuǎn)的可移植程序,必須使用volatile屬性。

  5. 使用異常機(jī)制不必每次調(diào)用都檢查一次返回值,但因?yàn)槌绦蛑腥魏挝恢枚伎赡軖伋霎惓#仨殨r(shí)刻考慮是否捕捉異常。在大型程序中,判斷是否捕捉異常會(huì)是很大的思維負(fù)擔(dān),影響開(kāi)發(fā)效率。

相比之下,通過(guò)返回值指示錯(cuò)誤有利于調(diào)用者在最近出錯(cuò)的地方進(jìn)行檢查。此外,返回值模式中程序的運(yùn)行順序一目了然,對(duì)維護(hù)者可讀性更高。因此,應(yīng)用程序中不建議使用setjmp/longjmp“異常處理”機(jī)制(除非庫(kù)或框架)。

2.5 信號(hào)(signal/raise)

在某些情況下,主機(jī)環(huán)境或操作系統(tǒng)可能發(fā)出信號(hào)(signal)事件,指示特定的編程錯(cuò)誤或嚴(yán)重事件(如除0或中斷等)。這些信號(hào)本意并非用于錯(cuò)誤捕獲,而是指示與正常程序流不協(xié)調(diào)的外部事件。

為處理信號(hào),需要使用以下信號(hào)相關(guān)函數(shù):

#include?
typedef?void?(*fpSigFunc)(int);
fpSigFunc?signal(int?signo,?fpSigFunc?fpHandler);
int?raise(int?signo);

其中,參數(shù)signo是Unix系統(tǒng)定義的信號(hào)編號(hào)(正整數(shù)),不允許用戶自定義信號(hào)。參數(shù)fpHandler是常量SIG_DFL、常量SIG_IGN或當(dāng)接收到此信號(hào)后要調(diào)用的信號(hào)處理函數(shù)(signal handler)的地址。若指定SIG_DFL,則接收到此信號(hào)后調(diào)用系統(tǒng)的缺省處理函數(shù);若指定SIG_ IGN,則向內(nèi)核表明忽略此信號(hào)(SIGKILL和SIGSTOP不可忽略)。

某些異常信號(hào)(如除數(shù)為零)不太可能恢復(fù),此時(shí)信號(hào)處理函數(shù)可在程序終止前正確地清理某些資源。信號(hào)處理函數(shù)所收到的異常信息僅是一個(gè)整數(shù)(待處理的信號(hào)事件),這點(diǎn)與setjmp()函數(shù)類似。

signal()函數(shù)執(zhí)行成功時(shí)返回前次掛接的處理函數(shù)地址,失敗時(shí)則返回SIG_ERR。信號(hào)通過(guò)調(diào)用raise()函數(shù)產(chǎn)生并被處理函數(shù)捕獲。

以除零錯(cuò)誤為例:

void?fphandler(int?dwSigNo)
{
????printf("Exception?is?raised,?dwSigNo=%d!\n",?dwSigNo);
}
int?main(void)
{
????if(SIG_ERR?==?signal(SIGFPE,?fphandler))
????{
????????fprintf(stderr,?"Fail?to?set?SIGFPE?handler!\n");
????????exit(EXIT_FAILURE);
????}

????double?fDividend?=?10.0,?fDivisor?=?0.0;
????if(0?==?fDivisor)
????{
????????raise(SIGFPE);
????????exit(EXIT_FAILURE);
????}
????printf("The?quotient?is?%.2lf\n",?fDividend/fDivisor);

????return?0;
}

執(zhí)行結(jié)果為"Exception is raised, dwSigNo=8!"(0.0不等同于0,因此系統(tǒng)未檢測(cè)到浮點(diǎn)異常)。

若將被除數(shù)(Dividend)和除數(shù)(Divisor)改為整型變量:

int?main(void)
{
????if(SIG_ERR?==?signal(SIGFPE,?fphandler))
????{
????????fprintf(stderr,?"Fail?to?set?SIGFPE?handler!\n");
????????exit(EXIT_FAILURE);
????}

????int?dwDividend?=?10,?dwDivisor?=?0;
????double?fQuotient?=?dwDividend/dwDivisor;
????printf("The?quotient?is?%.2lf\n",?fQuotient);

????return?0;
}

則執(zhí)行后循環(huán)輸出"Exception is raised, dwSigNo=8!"。這是因?yàn)檫M(jìn)程捕捉到信號(hào)并對(duì)其進(jìn)行處理時(shí),進(jìn)程正在執(zhí)行的指令序列被信號(hào)處理程序臨時(shí)中斷,它首先執(zhí)行該信號(hào)處理程序中的指令。若從信號(hào)處理程序返回(未調(diào)用exit或longjmp),則繼續(xù)執(zhí)行在捕捉到信號(hào)時(shí)進(jìn)程正在執(zhí)行的正常指令序列。

因此,每次系統(tǒng)調(diào)用信號(hào)處理函數(shù)后,異??刂屏鬟€會(huì)返回除0指令繼續(xù)執(zhí)行。而除0異常不可恢復(fù),導(dǎo)致反復(fù)輸出異常。

規(guī)避方法有兩種:

  1. 將SIGFPE信號(hào)變成系統(tǒng)默認(rèn)處理,即signal(SIGFPE, SIG_DFL)。

此時(shí)執(zhí)行輸出為"Floating point exception"。

  1. 利用setjmp/longjmp跳過(guò)引發(fā)異常的指令:
jmp_buf?gJmpBuf;
void?fphandler(int?dwSigNo)
{
????printf("Exception?is?raised,?dwSigNo=%d!\n",?dwSigNo);
????longjmp(gJmpBuf,?1);
}
int?main(void)
{
????if(SIG_ERR?==?signal(SIGFPE,?SIG_DFL))
????{
????????fprintf(stderr,?"Fail?to?set?SIGFPE?handler!\n");
????????exit(EXIT_FAILURE);
????}

????int?dwDividend?=?10,?dwDivisor?=?0;
????if(0?==?setjmp(gJmpBuf))
????{
????????double?fQuotient?=?dwDividend/dwDivisor;
????????printf("The?quotient?is?%.2lf\n",?fQuotient);
????}
????else
????{
????????printf("The?divisor?cannot?be?0!\n");
????}

????return?0;
}

注意,在信號(hào)處理程序中還可使用sigsetjmp/siglongjmp函數(shù)進(jìn)行非局部跳轉(zhuǎn)。相比setjmp函數(shù),sigsetjmp函數(shù)增加一個(gè)信號(hào)屏蔽字參數(shù)。

三 ?錯(cuò)誤處理

3.1 終止(abort/exit)

致命性錯(cuò)誤無(wú)法恢復(fù),只能終止程序。例如,當(dāng)空閑堆管理程序無(wú)法提供可用的連續(xù)空間時(shí)(調(diào)用malloc返回NULL),用戶程序的健壯性將嚴(yán)重受損。若恢復(fù)的可能性渺茫,則最好終止或重啟程序。

標(biāo)準(zhǔn)C庫(kù)提供exit()和abort()函數(shù),分別用于程序正常終止和異常終止。兩者都不會(huì)返回到調(diào)用者中,且都導(dǎo)致程序被強(qiáng)行結(jié)束。

exit()及其相似函數(shù)原型聲明如下:

#include?
void?exit(int?status);
void?_Exit(int?status);
#include?
void?_exit(int?status);

其中,exit和_Exit由ISO C說(shuō)明,而_exit由Posix.1說(shuō)明。因此使用不同的頭文件。

ISO C定義_ Exit旨在為進(jìn)程提供一種無(wú)需運(yùn)行終止處理程序(exit handler)或信號(hào)處理程序(signal handler)而終止的方法,是否沖洗標(biāo)準(zhǔn)I/O流則取決于實(shí)現(xiàn)。Unix系統(tǒng)中_ Exit 和_ exit同義,兩者均直接進(jìn)入內(nèi)核,而不沖洗標(biāo)準(zhǔn)I/O流。_exit函數(shù)由exit調(diào)用,處理Unix特定的細(xì)節(jié)。

exit()函數(shù)首先調(diào)用執(zhí)行各終止處理程序,然后按需多次調(diào)用fclose函數(shù)關(guān)閉所有已打開(kāi)的標(biāo)準(zhǔn)I/O流(將所有緩沖的輸出數(shù)據(jù)沖洗寫(xiě)到文件上),然后調(diào)用_exit函數(shù)進(jìn)入內(nèi)核。

標(biāo)準(zhǔn)函數(shù)庫(kù)中有一種“緩沖I/O(buffered I/O)”機(jī)制。該機(jī)制對(duì)于每個(gè)打開(kāi)的文件,在內(nèi)存中維護(hù)一片緩沖區(qū)。每次讀文件時(shí)會(huì)連續(xù)讀出若干條記錄,下次讀文件時(shí)就可直接從內(nèi)存緩沖區(qū)中讀?。幻看螌?xiě)文件時(shí)也僅僅寫(xiě)入內(nèi)存緩沖區(qū),等滿足一定條件(如緩沖區(qū)填滿,或遇到換行符等特定字符)時(shí)再將緩沖區(qū)內(nèi)容一次性寫(xiě)入文件。

通過(guò)盡可能減少read和write調(diào)用的次數(shù),該機(jī)制可顯著提高文件讀寫(xiě)速度,但也給編程帶來(lái)某些麻煩。例如,向文件內(nèi)寫(xiě)入一些數(shù)據(jù)時(shí),若未滿足特定條件,數(shù)據(jù)會(huì)暫存在緩沖區(qū)內(nèi)。開(kāi)發(fā)者并不知曉這點(diǎn),而調(diào)用_ _ exit()函數(shù)直接關(guān)閉進(jìn)程,導(dǎo)致緩沖區(qū)數(shù)據(jù)丟失。

因此,若要保證數(shù)據(jù)完整性,必須調(diào)用exit()函數(shù),或在調(diào)用_ _ exit()函數(shù)前先通過(guò)fflush()函數(shù)將緩沖區(qū)內(nèi)容寫(xiě)入指定的文件。

例如,調(diào)用printf函數(shù)(遇到換行符'\n'時(shí)自動(dòng)讀出緩沖區(qū)中內(nèi)容)函數(shù)后再調(diào)用exit:

int?main(void)
{
????printf("Using?exit...\n");
????printf("This?is?the?content?in?buffer");
????exit(0);
????printf("This?line?will?never?be?reached\n");
}

執(zhí)行輸出為:

Using?exit...
This?is?the?content?in?buffer(結(jié)尾無(wú)換行符)

調(diào)用printf函數(shù)后再調(diào)用_exit:

int?main(void)
{
????printf("Using?_exit...\n");
????printf("This?is?the?content?in?buffer");
????fprintf(stdout,?"Standard?output?stream");
????fprintf(stderr,?"Standard?error?stream");
????//fflush(stdout);
????_exit(0);
}

執(zhí)行輸出為:

Using?_exit...
Standard?error?stream(結(jié)尾無(wú)換行符)

若取消fflush句注釋,則執(zhí)行輸出為:

Using?_exit...
Standard?error?streamThis?is?the?content?in?bufferStandard?output?stream(結(jié)尾無(wú)換行符)

通常,標(biāo)準(zhǔn)錯(cuò)誤是不帶緩沖的,打開(kāi)至終端設(shè)備的流(如標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出)是行緩沖的(遇換行符則執(zhí)行I/O操作);其他所有流則是全緩沖的(填滿標(biāo)準(zhǔn)I/O緩沖區(qū)后才執(zhí)行I/O操作)。

三個(gè)exit函數(shù)都帶有一個(gè)整型參數(shù)status,稱之為終止?fàn)顟B(tài)(或退出狀態(tài))。該參數(shù)取值通常為兩個(gè)宏,即EXIT_SUCCESS(0)和EXIT_FAILURE(1)。大多數(shù)Unix shell都可檢查進(jìn)程的終止?fàn)顟B(tài)。

若(a)調(diào)用這些函數(shù)時(shí)不帶終止?fàn)顟B(tài),或(b)main函數(shù)執(zhí)行了無(wú)返回值的return語(yǔ)句,或(c) main函數(shù)未聲明返回類型為整型,則該進(jìn)程的終止?fàn)顟B(tài)未定義。但若main函數(shù)的返回類型為整型,且執(zhí)行到最后一條語(yǔ)句時(shí)返回(隱式返回),則該進(jìn)程的終止?fàn)顟B(tài)為0。

exit系列函數(shù)是最簡(jiǎn)單直接的錯(cuò)誤處理方式,但程序出錯(cuò)終止時(shí)無(wú)法捕獲異常信息。ISO C規(guī)定一個(gè)進(jìn)程可以注冊(cè)32個(gè)終止處理函數(shù)。這些函數(shù)可編寫(xiě)為自定義的清理代碼,將由exit()函數(shù)自動(dòng)調(diào)用,并可使用atexit()函數(shù)進(jìn)行注冊(cè)。

#include?
int?atexit(void?(*func)(void));

該函數(shù)的參數(shù)是一個(gè)無(wú)參數(shù)無(wú)返回值的終止處理函數(shù)。exit()函數(shù)按注冊(cè)的相反順序調(diào)用這些函數(shù)。同一函數(shù)若注冊(cè)多次,則被調(diào)用多次。即使不調(diào)用exit函數(shù),程序退出時(shí)也會(huì)執(zhí)行atexit注冊(cè)的函數(shù)。

通過(guò)結(jié)合exit()和atexit()函數(shù),可在程序出錯(cuò)終止時(shí)拋出異常信息。以除零錯(cuò)誤為例:

double?Division(double?fDividend,?double?fDivisor)
{
????return?fDividend/fDivisor;
}
void?RaiseException1(void)
{
????printf("Exception?is?raised:?\n");
}
void?RaiseException2(void)
{
????printf("The?divisor?cannot?be?0!\n");
}

int?main(void)
{
????double?fDividend?=?0.0,?fDivisor?=?0.0;
????printf("Enter?the?dividend:?");
????scanf("%lf",?&fDividend);
????printf("Enter?the?divisor?:?");
????scanf("%lf",?&fDivisor);
????if(0?==?fDivisor)
????{
????????atexit(RaiseException2);
????????atexit(RaiseException1);
????????exit(EXIT_FAILURE);
????}
????printf("The?quotient?is?%.2lf\n",?Division(fDividend,?fDivisor));

????return?0;
}

執(zhí)行結(jié)果為:

Enter?the?dividend:?10
Enter?the?divisor?:?0
Exception?is?raised:?
The?divisor?cannot?be?0!

注意,通過(guò)atexit()注冊(cè)的終止處理函數(shù)必須顯式(使用return語(yǔ)句)或隱式地正常返回,而不能通過(guò)調(diào)用exit()或longjmp()等其他方式終止,否則將導(dǎo)致未定義的行為。

例如,在GCC4.1.2編譯環(huán)境下,調(diào)用exit()終止時(shí)仍等效于正常返回;而VC6.0編譯環(huán)境下,調(diào)用exit()的處理函數(shù)將阻止其他已注冊(cè)的處理函數(shù)被調(diào)用,并且可能導(dǎo)致程序異常終止甚至崩潰。

嵌套調(diào)用exit()函數(shù)將導(dǎo)致未定義的行為,因此在終止處理函數(shù)或信號(hào)處理函數(shù)中盡量不要調(diào)用exit()。

abort()函數(shù)原型聲明如下:

#include?
void?abort(void);

該函數(shù)將SIGABRT信號(hào)發(fā)送給調(diào)用進(jìn)程(進(jìn)程不應(yīng)忽略此信號(hào))。

ISO C規(guī)定,調(diào)用abort將向主機(jī)環(huán)境遞送一個(gè)未成功終止的通知,其方法是調(diào)用raise(SIGABRT)函數(shù)。因此,abort()函數(shù)理論上的實(shí)現(xiàn)為:

void?abort(void)
{
????raise(SIGABRT);
????exit(EXIT_FAILURE);
}

可見(jiàn),即使捕捉到SIGABRT信號(hào)且相應(yīng)信號(hào)處理程序返回,abort()函數(shù)仍然終止程序。Posix.1也說(shuō)明abort()函數(shù)并不理會(huì)進(jìn)程對(duì)此信號(hào)的阻塞和忽略。

進(jìn)程捕捉到SIGABRT信號(hào)后,可在其終止之前執(zhí)行所需的清理操作(如調(diào)用exit)。若進(jìn)程不在信號(hào)處理程序中終止自己,Posix.1聲明當(dāng)信號(hào)處理程序返回時(shí),abort()函數(shù)終止該進(jìn)程。

ISO C規(guī)定,abort()函數(shù)是否沖洗輸出流、關(guān)閉已打開(kāi)文件及刪除臨時(shí)文件由實(shí)現(xiàn)決定。Posix.1則要求若abort()函數(shù)終止進(jìn)程,則它對(duì)所有打開(kāi)標(biāo)準(zhǔn)I/O流的效果應(yīng)當(dāng)與進(jìn)程終止前對(duì)每個(gè)流調(diào)用fclose相同。為提高可移植性,若希望沖洗標(biāo)準(zhǔn)I/O流,則應(yīng)在調(diào)用abort()之前執(zhí)行這種操作。

3.2 斷言(assert)

abort()和exit()函數(shù)無(wú)條件終止程序。也可使用斷言(assert)有條件地終止程序。

assert是診斷調(diào)試程序時(shí)經(jīng)常使用的宏,定義在內(nèi)。該宏的典型實(shí)現(xiàn)如下:

#ifdef????NDEBUG
????#define?assert(expr)????????((void)?0)
#else
????extern?void?__assert((const?char?*,?const?char?*,?int,?const?char?*));
????#define?assert(expr)?\
????????((void)?((expr)?||?\
?????????(__assert(#expr,?__FILE__,?__LINE__,?__FUNCTION__),?0)))

#endif

可見(jiàn),assert宏僅在調(diào)試版本(未定義NDEBUG)中有效,且調(diào)用__assert()函數(shù)。該函數(shù)將輸出發(fā)生錯(cuò)誤的文件名、代碼行、函數(shù)名以及條件表達(dá)式:

void?__assert(const?char?*assertion,?const?char?*?filename,
??????????????int?linenumber,?register?const?char?*?function)
{
????fprintf(stderr,?"?[%s(%d)%s]?Assertion?'%s'?failed.\n",
????????????filename,?linenumber,
????????????((function?==?NULL)???"UnknownFunc"?:?function),
????????????assertion);
????abort();
}

因此,assert宏實(shí)際上是一個(gè)帶有錯(cuò)誤說(shuō)明信息的abort(),并做了前提條件檢查。若檢查失敗(斷言表達(dá)式為邏輯假),則報(bào)告錯(cuò)誤并終止程序;否則繼續(xù)執(zhí)行后面的語(yǔ)句。

使用者也可按需定制assert宏。例如,另一實(shí)現(xiàn)版本為:

#undef?assert
#ifdef?NDEBUG
????#define?assert(expr)????????((void)?0)
#else
????#define?assert(expr)????????((void)?((expr)?||?\
?????????(fprintf(stderr,?"[%s(%d)]?Assertion?'%s'?failed.\n",?\
?????????__FILE__,?__LINE__,?#expr),?abort(),?0)))

#endif

注意,expr1||expr2表達(dá)式作為單獨(dú)語(yǔ)句出現(xiàn)時(shí),等效于條件語(yǔ)句if(!(expr1))expr2。這樣,assert宏就可擴(kuò)展為一個(gè)表達(dá)式,而不是一條語(yǔ)句。逗號(hào)表達(dá)式expr2返回最后一個(gè)表達(dá)式的值(即0),以符合||操作符的要求。

使用斷言時(shí)應(yīng)注意以下幾點(diǎn):

  1. 斷言用于檢測(cè)理論上絕不應(yīng)該出現(xiàn)的情況,如入?yún)⒅羔槥榭铡⒊龜?shù)為0等。

對(duì)比以下兩種情況:

char?*Strcpy(char?*pszDst,?const?char?*pszSrc)
{
????char?*pszDstOrig?=?pszDst;
????assert((pszDst?!=?NULL)?&&?(pszSrc?!=?NULL));
????while((*pszDst++?=?*pszSrc++)?!=?'\0');
????????return?pszDstOrig;
}
FILE?*OpenFile(const?char?*pszName,?const?char?*pszMode)
{
????FILE?*pFile?=?fopen(pszName,?pszMode);
????assert(pFile?!=?NULL);
????if(NULL?==?pFile)
????????return?NULL;

????//...
????return?pFile;
}

Strcpy()函數(shù)中斷言使用正確,因?yàn)槿雲(yún)⒆址羔槻粦?yīng)為空。OpenFile()函數(shù)中則不能使用斷言,因?yàn)橛脩艨赡苄枰獧z查某個(gè)文件是否存在,而這并非錯(cuò)誤或異常。

2)assert是宏不是函數(shù),在調(diào)試版本和非調(diào)試版本中行為不同。因此必須確保斷言表達(dá)式的求值不會(huì)產(chǎn)生副作用,如修改變量和改變方法的返回值。不過(guò),可根據(jù)這一副作用測(cè)試斷言是否打開(kāi):

int?main(void)
{
????int?dwChg?=?0;
????assert(dwChg?=?1);
????if(0?==?dwChg)
????????printf("Assertion?should?be?enabled!\n");
????return?0;
}
  1. 不應(yīng)使用斷言檢查公共方法的參數(shù)(應(yīng)使用參數(shù)校驗(yàn)代碼),但可用于檢查傳遞給私有方法的參數(shù)。

  2. 可使用斷言測(cè)試方法執(zhí)行的前置條件和后置條件,以及執(zhí)行前后的不變性。

  3. 斷言條件不成立時(shí),會(huì)調(diào)用abort()函數(shù)終止程序,應(yīng)用程序沒(méi)有機(jī)會(huì)做清理工作(如關(guān)閉文件和數(shù)據(jù)庫(kù))。

3.3 封裝

為減少錯(cuò)誤檢查和處理代碼的重復(fù)性,可對(duì)函數(shù)調(diào)用或錯(cuò)誤輸出進(jìn)行封裝。

  1. 封裝具有錯(cuò)誤返回值的函數(shù)

通常針對(duì)頻繁調(diào)用的基礎(chǔ)性系統(tǒng)函數(shù),如內(nèi)存和內(nèi)核對(duì)象操作等。舉例如下:

pid_t?Fork(void)?//首字母大寫(xiě),以區(qū)分系統(tǒng)函數(shù)fork()
{
????pid_t?pid;
????if((pid?=?fork())<0)
????{
????????fprintf(stderr,?"Fork?error:?%s\n",?strerror(errno));
????????exit(0);
????}
????return?pid;
}

Fork()函數(shù)出錯(cuò)退出時(shí)依賴系統(tǒng)清理資源。若還需清理其他資源(如已創(chuàng)建的臨時(shí)文件),可增加一個(gè)負(fù)責(zé)清理的回調(diào)函數(shù)。

注意,并非所有系統(tǒng)函數(shù)都可封裝,應(yīng)根據(jù)具體業(yè)務(wù)邏輯確定。

  1. 封裝錯(cuò)誤輸出

通常需要使用ISO C變長(zhǎng)參數(shù)表特性。例如《Unix網(wǎng)絡(luò)編程》中將輸出至標(biāo)準(zhǔn)出錯(cuò)文件的代碼封裝如下:

#include?
#include?
#define?HAVE_VSNPRINTF??1
#define?MAXLINE?????????4096??/*?max?text?line?length?*/
int?daemon_proc;??/*?set?nonzero?by?daemon_init()?*/
static?void?err_doit(int?errnoflag,?int?level,?const?char?*?fmt,?va_list?ap)
{
????int?errno_save,?n;
????char?buf[MAXLINE?+?1];

????errno_save?=?errno;????/*?Value?caller?might?want?printed.?*/
#ifdef?HAVE_VSNPRINTF
????vsnprintf(buf,?MAXLINE,?fmt,?ap);
#else
????vsprintf(buf,?fmt,?ap);????/*?This?is?not?safe?*/
#endif
????n?=?strlen(buf);
????if?(errnoflag)?{
????????snprintf(buf?+?n,?MAXLINE?-?n,?":?%s",?strerror(errno_save));
????}
????strcat(buf,?"\n");

????if?(daemon_proc)?{
????????syslog(level,?buf);
????}?else?{
????????fflush(stdout);????/*?In?case?stdout?and?stderr?are?the?same?*/
????????fputs(buf,?stderr);
????????fflush(stderr);
????}

????return;
}

void?err_ret(const?char?*?fmt,?...)
{
????va_list?ap;

????va_start(ap,?fmt);
????err_doit(1,?LOG_INFO,?fmt,?ap);
????va_end(ap);

????return;
}

本文來(lái)源網(wǎng)絡(luò),免費(fèi)傳達(dá)知識(shí),版權(quán)歸原作者所有。如涉及作品版權(quán)問(wèn)題,請(qǐng)聯(lián)系我進(jìn)行刪除。

往期推薦

柔性數(shù)組在實(shí)際項(xiàng)目中的應(yīng)用?

《嵌入式Linux驅(qū)動(dòng)大全》

在公眾號(hào)聊天界面回復(fù)1024,可獲取嵌入式資源

?

嵌入式大雜燴 專注于嵌入式技術(shù),包括但不限于C/C++、嵌入式、物聯(lián)網(wǎng)、Linux等編程學(xué)習(xí)筆記,同時(shí),內(nèi)包含大量的學(xué)習(xí)資源。歡迎關(guān)注,一同交流學(xué)習(xí),共同進(jìn)步!
評(píng)論
  • 100 字導(dǎo)語(yǔ)工業(yè)防爆鎧裝線纜硬度高、結(jié)構(gòu)特殊,普通圓形防爆連接器密封結(jié)構(gòu)難以適配,極易出現(xiàn)夾不緊、滲水、線纜滑脫問(wèn)題。本文針對(duì)鎧裝線纜特性,匹配 4 芯、5PIN、8 芯防爆法蘭插座安裝工藝,明確鎧裝固定、密封壓緊、應(yīng)力釋放全套施工標(biāo)準(zhǔn)。關(guān)鍵詞:圓形防爆連接器,鎧裝線纜,防爆法蘭插座,防爆防水連接器,IP67,8 芯,防爆電源接頭,防爆接插件1 產(chǎn)品概述鎧裝線纜自帶金屬保護(hù)層,硬度大、不可隨意彎折,常規(guī)塑料密封套無(wú)法有效抱緊。很多現(xiàn)場(chǎng)直接使用普通格蘭密封鎧裝線,導(dǎo)致尾部密封失效、鎧裝層懸空松動(dòng)
    kyfbest科迎法電氣 2026-08-12 12:48 218瀏覽
  • 導(dǎo)語(yǔ):進(jìn)入8月,全球LCD TV面板采購(gòu)需求受年底大促備貨、以價(jià)換量策略雙重拉動(dòng)呈現(xiàn)邊際回暖,但修復(fù)力度整體有限。由于頭部面板廠持續(xù)維持高稼動(dòng)生產(chǎn),行業(yè)供需格局仍處寬松區(qū)間,面板價(jià)格整體延續(xù)小幅回落態(tài)勢(shì),尺寸結(jié)構(gòu)性分化特征持續(xù)顯現(xiàn)。CINNO Research 簡(jiǎn)評(píng)今年8月,全球 LCD TV面板市場(chǎng)呈現(xiàn)需求弱修復(fù)、供給高位運(yùn)行的特征,整體價(jià)格延續(xù)下行且結(jié)構(gòu)性分化顯著。需求端受年底大促備貨、以價(jià)換量策略拉動(dòng)邊際回暖,但修復(fù)力度有限且建立在以價(jià)換量的基礎(chǔ)上;供給端高世代線稼動(dòng)率維持85%以上,供需
    CINNOResearch 2026-08-12 11:00 144瀏覽
  • 一只光模塊在出廠前,要在-40℃的極寒、25℃的常溫、85℃的高溫之間反復(fù)“渡劫”。稍有不慎,輕則參數(shù)漂移,重則整批報(bào)廢——這就是三溫測(cè)試,高速光模塊量產(chǎn)前的最后一道生死線。引言:AI越熱,測(cè)試越難AI數(shù)據(jù)中心的爆發(fā)式增長(zhǎng),讓400G、800G甚至1.6T光模塊的需求如火箭般躥升。但鮮為人知的是:光模塊速率每翻一番,測(cè)試時(shí)間卻不降反升。一組殘酷的數(shù)據(jù)對(duì)比:速率單只模塊測(cè)試耗時(shí)測(cè)試設(shè)備帶寬要求400G約15-20分鐘50GHz800G約20-30分鐘50GHz1.6T約40-60分鐘≥65GHz設(shè)
    用戶1774416745629 2026-08-12 13:36 220瀏覽
  • 導(dǎo)語(yǔ):根據(jù)CINNO ? ePaper Insight數(shù)據(jù),2026年上半年,全球電子紙終端總銷量約2.9億臺(tái)(枚),同比增長(zhǎng)25%。其中,電子紙價(jià)簽終端銷量2.8億枚,同比增長(zhǎng)27%,繼續(xù)成為產(chǎn)業(yè)增長(zhǎng)的核心動(dòng)力;電子紙平板銷量610萬(wàn)臺(tái),同比下降超過(guò)16%,較2025年上半年730萬(wàn)臺(tái)有所回落。細(xì)分市場(chǎng)看,閱讀器銷量480萬(wàn)臺(tái),辦公本銷量130萬(wàn)臺(tái)。整體來(lái)看,電子紙應(yīng)用結(jié)構(gòu)進(jìn)一步向智慧零售集中,價(jià)簽高景氣與平板階段性調(diào)整形成明顯分化。全球終端銷量同比增長(zhǎng)25%,價(jià)簽貢獻(xiàn)主要增量CINNO ?
    CINNOResearch 2026-08-12 11:05 181瀏覽
  • 8月8日,國(guó)聯(lián)民生證券發(fā)布了一則公告,內(nèi)容表明公司董事會(huì)收到了非執(zhí)行董事楊振興提交的辭職報(bào)告,其因個(gè)人原因辭去公司非執(zhí)行董事這一職務(wù)。而且在辭職之后,楊振興將不再擔(dān)任國(guó)聯(lián)民生證券以及其控股子公司的任何職務(wù)。僅僅兩天之后,公司第六屆董事會(huì)第7次會(huì)議召開(kāi),這次會(huì)議一口氣通過(guò)了多項(xiàng)議案,包括審議通過(guò)《國(guó)聯(lián)民生證券股份有限公司“十五五”發(fā)展規(guī)劃(2026-2030)》、1億至2億元A股回購(gòu)方案以及為子公司民生證券提供不超過(guò)40億元的動(dòng)態(tài)凈資本擔(dān)保承諾等等。同時(shí),為優(yōu)化資源配置,公司還擬減少民生證券注冊(cè)資
    用戶1782919753097 2026-08-13 00:00 62瀏覽
  • 100 字導(dǎo)語(yǔ)現(xiàn)場(chǎng)防爆航插頻繁出現(xiàn)插拔卡頓、沙沙異響、對(duì)位卡死等問(wèn)題,大多不是產(chǎn)品質(zhì)量問(wèn)題,而是粉塵進(jìn)腔、觸點(diǎn)氧化、防呆鍵位磨損導(dǎo)致。本文結(jié)合 3 針 3 孔、4 芯、5PIN、8 芯圓形防爆連接器運(yùn)行特性,梳理異響卡頓故障根源,制定標(biāo)準(zhǔn)化清潔修復(fù)流程,保障防爆航空插頭對(duì)接順暢、密封到位。關(guān)鍵詞:圓形防爆連接器,防爆航空插頭,防爆端子接頭,防爆接插件,插拔卡頓,IP67,5PIN,8 芯,防爆傳感器航插接頭1 產(chǎn)品概述圓形防爆連接器公母頭依靠精密鍵位、冠簧插孔、密封面精準(zhǔn)配合實(shí)現(xiàn)對(duì)接。現(xiàn)場(chǎng)粉塵堆
    kyfbest科迎法電氣 2026-08-12 11:43 185瀏覽
  • 隨著AI算力需求的爆發(fā)式增長(zhǎng),400G、800G乃至1.6T高速光模塊正加速走向商用部署。在這場(chǎng)速率競(jìng)賽的背后,一個(gè)不容忽視的挑戰(zhàn)正浮出水面——硅光模塊對(duì)溫度的“敏感體質(zhì)” ,正在成為可靠性驗(yàn)證領(lǐng)域的新課題。一、為什么硅光模塊如此“怕熱怕冷”?傳統(tǒng)光模塊主要采用III-V族化合物半導(dǎo)體材料(如InP、GaAs),而硅光模塊則以硅基材料為核心,借助CMOS兼容工藝實(shí)現(xiàn)光電器件的單片集成。這種技術(shù)路線帶來(lái)了成本低、集成度高、功耗小的優(yōu)勢(shì),但也埋下了溫度敏感性的隱患。根源在于材料的熱膨脹系數(shù)
    用戶1774416745629 2026-08-13 09:40 54瀏覽
  • 100 字導(dǎo)語(yǔ)廠區(qū)停機(jī)檢修、設(shè)備長(zhǎng)期靜置存放,防爆航插易受潮、積塵、膠圈老化、觸點(diǎn)氧化。本文針對(duì)停機(jī)復(fù)工場(chǎng)景,制定 3 針 3 孔、5PIN、8 芯圓形防爆連接器全套檢測(cè)保養(yǎng)流程,明確絕緣、導(dǎo)通、密封、屏蔽復(fù)工核驗(yàn)標(biāo)準(zhǔn),避免復(fù)工跳閘、信號(hào)異常。關(guān)鍵詞:圓形防爆連接器,復(fù)工檢測(cè),防爆航空插頭,防爆法蘭插座,防爆接插件,IP67, 靜置保養(yǎng),5PIN,8 芯1 產(chǎn)品概述設(shè)備長(zhǎng)期停機(jī)靜置,防爆連接器腔體密閉不通風(fēng),極易積聚潮氣、凝結(jié)水汽;密封件長(zhǎng)期靜態(tài)受壓出現(xiàn)永久形變;鍍金觸點(diǎn)鈍化氧化。直接通電復(fù)工極
    kyfbest科迎法電氣 2026-08-12 13:32 219瀏覽
  • 在國(guó)內(nèi)大量通用制造產(chǎn)線與工業(yè)場(chǎng)站里,智能化升級(jí)正普遍面臨著的現(xiàn)實(shí)困境:小到產(chǎn)線外觀質(zhì)檢、設(shè)備狀態(tài)監(jiān)測(cè)終端,大到站房智能網(wǎng)關(guān)、工業(yè)人機(jī)交互設(shè)備,企業(yè)既想落地本地AI推理、多設(shè)備聯(lián)網(wǎng)協(xié)同等智能化功能,又難以承擔(dān)高端工控方案的高昂部署成本;既要求硬件能適應(yīng)高低溫、粉塵、電磁干擾的復(fù)雜工業(yè)環(huán)境,又受制于預(yù)算門(mén)檻退而選擇消費(fèi)級(jí)主控,最終往往陷入“改得起、用不住”的尷尬局面。這是當(dāng)前工業(yè)智能化向中端下沉過(guò)程中的共性痛點(diǎn)。一方面,高端算力方案性能過(guò)剩、單價(jià)居高不下,多設(shè)備批量部署時(shí)成本壓力陡增,而入門(mén)級(jí)主控又
    飛凌嵌入式 2026-08-12 08:52 172瀏覽
  • 我在唯創(chuàng)知音做門(mén)鎖語(yǔ)音方案這幾年,常被廠商問(wèn)到一個(gè)看似簡(jiǎn)單的問(wèn)題,門(mén)鎖里的語(yǔ)音到底要做什么。門(mén)鎖語(yǔ)音在門(mén)鎖里早就不是加個(gè)喇叭那么回事。它要管門(mén)鈴、提示音、貓眼對(duì)講、藍(lán)牙配網(wǎng)播報(bào)、離線喊一聲開(kāi)門(mén),還有大屏上的菜單動(dòng)效。這些功能分散在前板、后板、貓眼和 APP 四個(gè)部位,芯片怎么選,各部位完全不一樣。最基礎(chǔ)的一塊是門(mén)鈴和固定提示音。很多走量機(jī)型到現(xiàn)在還是一顆小容量語(yǔ)音芯片扛著,像 WTN 系列里的 WTN6006、WTN6020,F(xiàn)lash 從 低時(shí)長(zhǎng)到高時(shí)長(zhǎng)都有不同的芯片,協(xié)議相同可以無(wú)縫切換。它
    唯創(chuàng)知音語(yǔ)音芯片 2026-08-12 17:34 53瀏覽
  • 報(bào)警語(yǔ)音在工業(yè)設(shè)備里的難題做工業(yè)設(shè)備的工程師多半遇到過(guò)這種事。設(shè)備上要播的語(yǔ)音條數(shù)越來(lái)越多,正常開(kāi)機(jī)提示、運(yùn)行狀態(tài)播報(bào)、故障報(bào)警、維護(hù)提醒、安全警告,加起來(lái)幾十條甚至上百條。一開(kāi)始用小容量 OTP 芯片還能應(yīng)付,條數(shù)一多就不夠用了。換大容量芯片,成本又上去了。更大的麻煩在報(bào)警優(yōu)先級(jí)上。工業(yè)設(shè)備運(yùn)行時(shí),語(yǔ)音芯片可能正在播放一段正常運(yùn)行狀態(tài)提示,比如"設(shè)備運(yùn)行中,請(qǐng)勿靠近"。這時(shí)候突然檢測(cè)到故障,需要立刻插播一條緊急報(bào)警語(yǔ)音"溫度過(guò)高,請(qǐng)立即停機(jī)檢查"。傳統(tǒng)語(yǔ)音芯片做不到這件事。它要么得先發(fā)停止命令
    唯創(chuàng)知音語(yǔ)音芯片 2026-08-12 17:50 62瀏覽
我要評(píng)論
0
點(diǎn)擊右上角,分享到朋友圈 我知道啦
請(qǐng)使用瀏覽器分享功能 我知道啦
主站蜘蛛池模板: 雪中悍刀行全集观看| 嫌疑人x的献身电影| 逆天狂妃:邪王靠边站全集免费 | 国家地理频道| 初体验5电影免费观看完整版| 心里的声音 迅雷下载| 《我的初恋不是姐姐》动漫| 金枝玉叶分集剧情介绍| 春雨电视剧高清在线播放视频| 理发店里的特殊待遇电影 | 高压监狱观看完整免费高清原声满天星2019| 《灰猎犬号》国语版| 嘻游记 高清完整版| 《裸体按摩师2》电影在线观看| 辐射美剧| 年轻的母亲无删减| 铁血战士3| 美乃雀电影| 女神宿舍的宿官君| 神犬小七第三季免费观看完整版| 美妙的旋律| 左旋肉碱有用么| 和田市| 女医生体检| 正宗泡菜制作方法教程| 俘虏之锁1-2动漫更新时间| 红发女郎下载| 龙川县| 输赢电视剧全集在线观看完整版 | 包青天孝子章洛| 假面超人rx全集| 旗开得胜集体舞| 林正英僵尸全集国语| 八尺夫人满天星法版在线观看| 出狱脱节| 甲壳虫汁| 新红楼梦徐锦江| 美女武士国语| 长月烬明电视剧全集| 电影《邻家有爱1》在线观看| 电视剧苦菜花| 等不及在车里做一次核酸检测 | 辽阳县| 陈情令免费观看完整版| 叶子楣电影《聊斋》高清国语版 | 天海翼香汗女教师在线播放-手机高清免费国语メイリン モデルコレクション-SL2 | 国风按摩院免费观看平台| 《狙击精英:巅峰对决》| 仓井空电影| 人心俱乐部电视剧在线观看免费| 闯关东电视连续剧| 鬼电影国语版| 《壮志凌云》满天星在线| 孙中山在线观看| 插曲的痛60集全剧情介绍| 女大学生的沙龙室观看| 拼图电视剧| 谍战剧《惊变》在线看全集| 乘胜狙击粤语百度云| 元谋县| 惊情电影| 战狼6女版免费观看高清版 | 旧版封神榜| 卢旺达的玫瑰在线| 神马四兄弟电影| 南沙群岛在线| 与部长出差免费观看| 极乐老人| 刀郎北京演唱会| 最后的武林| 法国空乘2016不难空乘在线观看| 电视剧马向阳下乡记| 高清《女教师》未删减| 小野纱里奈| 韩国黄色三级片村故| 回到三国国语| 按摩乳房的电影| 深情眼在线观看电视剧| 《爱我几何》正版美国| 善良的女儿| 《女子护卫队满天星》全集免费观看| 电视红毯先生在线观看免费| 免费观看电影克莱尔的迷宫| 花木兰妆容太丑| 福秀回来了在线观看 | 乡村爱情圆舞曲大结局| 绝命狙击电影免费观看完整版| 扑杀天使朵库萝| 电影好家伙| 机械师2:复活 电影天堂| 酒吧激战| 剑啸江湖国语| 妈妈的职业2在线观看| 痴汉系列a| 87版《女的心愿》李丽英免费观看 | 四个少妇精油按摩| 黑色的眼泪,麻生香织| 美丽教师满天星| 迷你世界全皮肤破解版下载| 我爱男保姆电视剧| 东北黑道风云| 生活有点甜| 恐怖游轮高清| 《速度与激情10》电影| 冷箭电视剧| 终末的女武神在线观看| 法国禁忌| 健身小辣椒J3被草| 从此不负小相思短剧免费观看| 青春环游记第四季2023| 毒液2电影免费版在线观看| 若亚方舟| 西游记86版全集免费播放| 传闻中的陈芊芊电视剧在线观看| 美国爸爸要播种免费看| 新还珠格格的所有歌曲| 《丈夫叫妻子陪上司睡电影| 小爸爸爱奇艺| 美容院特殊待遇在线观看 | 《水润人生2》刘志贤| 免费电影网站在线观看完整版| 《激战丛林》电影观看| 春心荡漾369| 加勒比海盗完整版1| 大话西游之月光宝盒粤语| 一夜情电影| 沉香如屑·沉香重华在线观看| 胜算电视剧免费完整版| 乡村爱情5小夜曲全集| 蜂脸女人| 法国空姐5在线看免费高清满天星全集 | 和女部长出差免费观看| 蜘蛛侠:平行宇宙 电影| 喜爱夜蒲高清完整版| 庆余年第二季免费观看在线播放| 女大学沙龙| 禁止想象电影完整版| 空之色水之色 快播| 《新员工入职秘书2》| 微笑的闪士| 《女兵俘虏营》在线观看免费完整版| 《格林童话》免费观看| jizz日本16| 神级选择:每次都选对全集免费| 抽曲的痛高清免费视频| 摩登家庭6 电视剧| 东方在线观看| 闪闪发光的你第二季综艺| 超级战舰 百度影音| 使徒行者3大结局| 东陵大盗电视剧| 牙医郝板栗未删减版| 房奴试爱韩剧免费| 韩国女空调维修工| 直播:男子双杠决赛| 免费观看罗丽星克莱尔的电影| 逆风野兽电影免费观看| 电视剧周生如故全集免费观看| 龙帅都市归来:再掀风云短剧全集| 暮光之城 破晓| 李连杰笑傲江湖| 电影意大利种马| 女超人麦乐迪在线完整版| 《那山那海》今晚开播| 《21世纪爱情指南》免费观看| 女人的村庄电视剧全集| 落魄贵族琉璃川在线观看免费动漫 | 盛唐风流完整免费观看| hodv-21134| 十四岁姑娘免费观看电视剧剧情| 济公活佛第二部全集| 凯·帕克电影在线观看| 戏台电影陈佩斯抢先版免费看| 林深见鹿电视剧40集免费观看| 躲避的爱说唱词| 又见一帘幽梦吻戏| 彩云之南舞蹈视频| 亲本家人| 草帽警察全集在线观看| 日本的妈妈| 国外RAPPER小女孩| 《伪装者》全集免费高清在线观看| 凯登克罗斯《急诊护士》| 龙岭迷窟2022在线观看| 不再沉默| 黑蝴蝶刘敏涛完整版在线观看| 成事在人HD高清在线| 不戴胸罩叫邻居修灯泡| 赤狐书生免费观看全集| 振动器酷刑2| 食尚玩家| 《美国式禁忌3》中文| 假面骑士01全集免费观看完整版| 公的浮之手中字在线观看 | 回复术士的重启人生在线| 继拇2| 告诉我你想要什么 电影免费观看| 潜入搜救官完整版在线观看| 满江红在线观看免费观看完整版| 八仙饭店埃博拉病毒高清| 我成了他的班主任| 三年全免费观看在线播放| 玛丽的激情120分钟在线观看| 丈夫的属下中字2| 小猪佩奇的动画片| 美景之屋4| 燃冬电影高清在线观看免费| 瓜达卢奥的玫瑰免费播放完整版电影| 一扑二主全集| 特殊游泳私人教练在线观看 | 领导挺进娇妻身体| yellow在线精品视频免费观看| 门事件曝光国产在线| HD版《激战丛林》电影| 猫头鹰餐厅| 公公的之浮手| 父母爱情电视剧在线观看| 乘龙怪婿第一部| 电影湄公河行动| 你是我的荣耀32集免费观看| 千王之王 周星驰| 大地电影在线观看| 侠探杰克2迅雷下载| 天龙八部钟汉良| 末路狂花钱免费观看| 燃冬免费观看完整版电视剧全集| 刘敏涛老公常乐| 绑架大明星在线观看| 星语心愿伴奏| 儿子爱上妈妈| 跑男第二季在线观看| 旗开得胜下一句怎么接| 搭错车全集| 恋爱先生电视剧在线观看完整版| 销售的销售秘密2HD中字国 | 环太平洋1| 哇嘎电影免费高清在线| 与凤行更新时间| 卖房子的女销售完整版| 蜡笔小新1~9季恐怖剧集 | 韩剧妻子的情人| 安达莉子| 你是我兄弟36| 熊出没之过年爱奇艺| 韩国爱情男女电影免费在线观看| 一个好妈妈高清完整版| 人猿泰山h版字幕| 潜行狙击国语版| 海底原唱| 关中往事30集连续剧免费播放| 高清花样少年少女| 甄子丹锦衣卫| 姐姐来了在线免费观看| 同事三分亲国语全集| 壹号皇庭3| 《快乐至死》完整版免费观看| 白日梦奇遇记| 电影野性的呼唤巴克犬完整版| 刘亦菲的乳房| 神话电视剧土豆网| 太阳阶梯| 爱情公寓第三季第二集| 梦魇绝镇第二季| 步步惊心在线| 锤锤小矮人| 年轻漂亮大胸继拇2| 河套宽频| 梅姨案人贩子家属责怪申军良| 快播电影播放器官方下载| 再爱一次| 邻居也疯狂全集在线观看| 断金在线观看免费高清国语 | 小爸爸全集在线观看| 《村枝》周弘版高清版| 第九禁区| 男按摩师伦理片| 岳母的幸福生活百度影音| 《大度》韩世雅和谁在一起了| 上海上海电视剧| 森奈奈子在线| 电视剧日出日落| 公之手1| 小东西逃跑是要受到惩罚的| 羽月希在线观看| 纯情罗曼史第一季02| 女员工的付出5 中字| 古天乐意外电影在线播放免费| 食物链1-100大尺寸| 洛丽塔在线观看高清免费版| 瓜达卢佩的玫瑰免费播放完整版电影| 三男强一女90分钟在线观看| 娜妲莉的情人百度影音| 鬼吹灯之龙王五太子墓| 淑女欲望日记| 久久偷看各类WC女厕嘘嘘偷窃| 斗罗大陆合集| 台湾怀旧绝版酋长的女儿| 乡村牧师| 《睫毛膏》9| 釜山行西瓜影音| 银屏梅电视剧免费观看| 斗罗大陆更新至241集| 高清《斛珠夫人》免费观看| 末世女强全集免费| 酷吧电影网| 须要爸爸播种电影在线播放| 猫和老鼠东北方言版| 铜铜钢铿锵锵锵锵锵锵免费观看高清| 韩剧哦亲爱的| xl司令全集在线观看免费播放| 唐人街影院| 《新员工入职秘书2》| 举起手来第二部| 从裸开始| 一点就到家免费观看| 雪中悍刀行第2部免费观看| 变身动漫| rhts 040| 异形大战铁血战士电影| 特种部队2百度影音| 黑巨吊视频手机在线免费播放| 唐人街探案3正片完整版免费观看| 私人航空免费观看完| 普门品全文念诵及回向全文| 慢性呕吐实验室原版| 失控玩家豆瓣| 和讨厌的部长出差电影| 导火线粤语| 激战5李丽珍和谁睡了| 书卷一梦电视剧免费观看完整版| 小小少女16| 第36部分夫妇交换系列| 刘墉语录| 东北女不戴套在线看| 《万》观看完整版| 策驰影院免费| 电影甲午风云| 小马宝莉动画片全集免费中文版| 日本大妈| 和女部长出差免费观看| 日本电影牙医姐妹在线观看| 《灭火宝贝| 电影急诊护士完整版| 唐人街探案2免费观看高清版| 82板杨敏思版本1-5电视剧免费观看就是 | 一生无悔歌词| 人猿泰山无删减版1995主演| 大耳朵图图之小小欢乐魔法师全集| 黑白配hd1080完整版高清| 大力女子都奉顺| 我年轻的瘦子5| 疯狂女人| 辉夜大小姐想让我告白剧场版| 步步登顶:宫斗后位全集免费| 美国恐怖故事1电视剧| 我喜欢的样子你都有短剧免费观看| 王贵与安娜在线观看| 麦子交换| 诺言郭有才歌曲| 电影《丰年经继坶3》解析| av收藏之家| 日本修理工的艳遇| 爱情真善美第二部全集| 冰剑的魔术师将要一世界动漫| 骨语2完整版在线观看| 一起再看流星雨| 巜饥渴的人妻与维修师傅电影 | 三年全在线观看免费高清版| 战至巅峰3免费观看| 电梯小姐动漫3在线观看| 左右免费| 运钞大劫案电影免费观看国语| 玩命爱一个姑娘完整版| 淬火年代电视剧免费观看完整版| 黑白配中文完整版免费看| 关于未知的我们| ssis338最高の爱人沼在线完整| 澳门风云在线观看| 黑白配国语版在线播放| 宫斗逆袭短剧全集| 偷香高手| 元敬韩国电影免费观看| 蹉跎岁月主题歌| 意大利八尺夫人满天星全集剧情介绍| 洛丽塔1997| 需要爸爸播种美国第二季| 济南战役电影| 《售楼小姐》| 开心闯龙年大电影完整版| 8毫米2| 谁日过女副市长姜保红| 高达00第二季国语版| 女保险公司推销员4| 精忠岳飞69| 电影安斋拉拉中文字幕| 为时已是寿司!?| 坎贝奇的《品味人生》| 三世情缘国语版| 哪吒之魔童降世在线观看| 大熊喷哭自己| 太极 电影| 波多野吉衣销魂120分钟电影| 星球大战原力释放| 《激战丛林》完整版| 国模夏馨雨| 《法国空乘5》| 大江大河第2部免费播放| 云霓之望 泰剧中字| 牧羊曲伴奏| 淑蓉又痒了办公室献身| 新婚之喜台湾版主演| my盛lady电视剧| 法国女超人电影| 蔡妍 两个人 mv| 爱情攻略高清完整版粤语| 疯狂的麦克斯| 北上电视剧免费观看完整版| 《无憾》完整在线观看免费版坎贝奇| 解语花电影完整版在线| 《我的游泳女教练》电影| 黑girl小蛮| 总是很可爱第一季免费观看| 变形金刚动画片| 择天记一共多少集| 四位人妇精油按摩的功效电影| 幸福到万家全集免费观看网| 厨房的岳 90分钟电影| 涩谷二十四小时| 善良的嫂子手机在线观看| 魔都精兵的奴隶 第二季| 敢死队电影国语版| 需要爸爸的种子免费在线观看中文字幕 | 周生如故在线| 中国vs澳大利亚录像| 冢本战争电影免费观看| 落水狗qvod| 理论片2018理论| 《有夫之妇的秘书》演员表| 高清《时光与你都很甜》大结局| 陈雅伦《为卿痴狂》| 和平精英火箭小子攻略| 固镇县| 串烧50首| 天堂之囚电影完整版中文| 越南版的《性的暴行》在线 | 快递员的特殊待遇| 西游记后传高清| 奔跑吧兄弟第三季第三期| 8号当铺电视剧免费观看| 日韩mmm| 巴萨战胜曼联| 妻子6韩国免费完整版电视| 情事2014韩世雅电影在线观看| 燃罪电视剧在线观看全集免费播放| 《女教师3在线播放》电影在线观看| 邪恶力量第四季| 台湾绝版渔夫荒野| 破浪男女| 坠落女律师完整版| 可人儿成人社区| 大湾仔的夜第二季在线观看| 朝国年经继拇11| 黑人RAPPER大全 顶级| 美国禁忌睫毛膏4完整版她说视频| 莲花楼超前点播| 名捕震关东电视剧全集免费观看| 加菲猫家族国语版在线观看免费| 电影灰姑娘的故事2| 巜同学聚会》2| 娜塔莉在线观看| 小乌酱黑白双丝脚足在线播放| 高清《仙逆剧场版 神临之战》电影 | 通天大盗| 鬼五虐电影完整版在线观看| 禁忌女孩第三季免费观看| 伏罗希洛夫射手 电影| 《品味人生》电影在线播放| 电影黄金大劫案| 冰湖重生全集观看| 恋空电影版| 日本电影少女免费观看电影大全| 小小水蜜桃6电视剧全集在线观看| 红场阅兵2025| 爱情公寓3 13| 八仙全传下载| 闵度允韩剧中文在线观看| 巜商务旅行女老板戴绿色帽的| 曹达华电影| 影子的诅咒电影在线观看| 爱人同志免费观看全集完整版| 老虎出更| 美国伦理片想要爸爸的种子在线看| 上海滩简谱| 黑白配国语版超清1080p高清| 薄冰谍战电视剧全集高清免费观看| 开心超人2| 女超人满天星无删减麦乐迪版在线播放 | 十七岁免费观看日本| 手工制作丝网花| 梦幻军事地区满天星1080P| 聊斋艳谭之婴宁电影完整版| 好妈妈高清在线播放中文| 媛崽好困原视频完整版| 电影红旗谱| 原始部落台湾版酋长女儿| 理想之城大结局| 骆驼祥子高清电影免费观看完整版| 渔夫荒淫史免费观看| 筱崎美沙| 昆明市| 聊斋艳谭在线观看| 南京照相馆电影免费观看高清| 《灭火行动》免费观看| 九子夺嫡全40集电视剧| 电视剧亲家| 麻生香织和黑人无删减版观看| 喜悦月饼批发| 第一个无产阶级政党| 女佐罗在线看| 结婚吗好的电视剧| 庆余年第三季40集免费观看| 黑草莓帝国| 海派甜心舞蹈| 需要爸爸种子 - 在线观看| 闯入17天| 《特战荣耀2》免费看| 开关女孩第二季| 莉娜安德森的满天星代表《小姐》 | 妹汁全集在线观看国语| 爸爸来播种完整版| 假面骑士极狐剧场版免费观看完整版| 亲爱的妈妈4韩国在线观看| 笑傲江湖更新| 大胸年轻继拇5免费| 杰伊比姆| 游龙惊凤电视剧| 列兵大学生| 3d柔铺团之极宝乐鉴宝鉴电影中文百度网盘 | 三年大片在线观看免费观看国语完整版| 庆余年电视剧全集资源| 听说你喜欢我电视剧免西瓜| 农民影视VIP免费观看电视剧| 法国空少免费观看| 《甜蜜的惩罚》| 爱情填满空白第二季完整版| 《今夜不设防》电视剧免费观看 | 韩版恶作剧之吻2| 电视剧《下海》| 宫锁沉香电视剧| 莫妮卡《爱我几何》未简版电影免费 | 风流女管家在线观看| mm的职业2免费观看完整版电影| 危情实录第二部电视剧| 上海滩简谱| 小卖部开悬崖上| 《女同学的滋味2在线观看》剧情1080P最新全集观看 -全集偶像剧 -黑蚂蚁影院 | 《地府阎王》免费观看| 陈宝莲《武则天秘史》免费看全集| 朱莉亚安妮《售楼女王》在线观看| 韩剧没有你活不下去| 斗罗大陆动漫全集| 《交换3》| 电影《霸占》在线观看| 小小诊所| 拼桌未删减| 指环王在线观看| 战狼六免费观看网站| 《斗罗大陆》全集完整版免费观看 | 逆袭之爱情敌电视剧| 要妈妈播种的1983年电影 | 齐天大圣孙悟空陈浩民版| 正在播放:丈夫不在家的5天之间,被命令禁欲直到初夜为止的我身心都被性豪的公 | 含泪活着下载| 躺床上丝袜老阿姨| 社长夫人还债电影免费观看| 在线观看蜜桃17| 罪与罚作者| 西安地铁保安女子完整版| 浪客行高清| 禁忌5在线免费观看| 爱的精灵 李丽珍| 肉食日未删减| 斗罗大陆3龙王传说第四季免费观看| 岳母的味道| 哇嘎免费观看在线播放高清| 韩剧保姆| 回响电视剧剧情介绍| 拯救者电视剧| 《她被搬运工侵犯》电影观看| 小妻太水嫩陆教授花式宠免费观看 | 官人我要之极乐宝鉴2| 神龙赌圣| 僵尸飞鲨免费播放| 超碰进入离开| 人生路不熟免费观看2023年 | 春雨电视剧高清在线播放| 酒店1-90集在线观看免费高清 | 爱我多深没删除版| 尼基塔 电影| 魔法科高中的优等生| 王妃太狂野| 公之孚手中字50| 成人版巜女超人H | 龙门镖局2电视剧全集| XL司令在线观看免费高清全集| 和田县| 火口的二人| 总裁的独家私宠全集免费| 回复术士第三集真人版| 丐世红颜短剧免费观看全集| 黄子华栋笃笑2018| 酒店1-5集免费观看全集| 男人女人一起愁愁愁电视剧免费观看动漫| 善意的竞争电视剧在线播放| 正在播放:【夏晴子&温芮欣】国风按摩院_年节双飞俏佳丽 | 王雪晶新年歌曲| 麦乐迪家庭矛盾完整版在线观看| 少年班 电影| 内地版流星花园| 郝板栗《千金小姐》免费完整版| 《为有暗香来》| 花子君第二季免费观看| 哇嘎电影免费播放网站大全| 怒火街头2国语下载| another动漫| 爱我几何免费完整版| 电视剧突围全集播放| 美国灭火宝贝完整版| 奇妙萌可之甜蜜捕获| 凌云志志(法国)满天星在线| 终极格斗5博伊卡高清| 王保长歪传高清| 宜宾市| 《超女麦乐迪》在线播放| 三十而立电视剧免费观看全集| 高清《监狱女囚》13集观看| 克罗斯凯登电影作品| 超级战舰国语版| 坏哥哥集百万部电影| 非缘勿扰电视剧下载| 热带暴风雨行动满天星版| 我是刑警38集在线观看| 星汉灿烂免费观看全集| 幸福爱人大结局| 动感之星精灵187完整版| 韩剧《我的家教老师》剧情介绍| 少年派电视剧全集免费| 凯登克罗斯在线观看 | 最后的格格全集| 最近韩国电影免费观看| 干妈在线| 361电影院| 火影忍者论坛52pk| 新金银悔5一10普通话| 星际大战动画片| 独一无二在线观看完整版| 多情成仁高清影视首页| 360大佬爱上我第二季全集观看 | 好玩的还是瘦子韩剧| 吃鸡的段位等级排列顺序| 《姐妹新娘》全集观看| 星际穿越全集免费| 日韩伦理片 儿媳妇的味道| 奇妙的美发沙龙2| 美国惊悚片无人生还2012| 爸爸的种子| 冰雪奇缘成人片在线观看| 桃花请缨免费看全集| 一生一世谢霆锋高圆圆| 凯·帕克《满天星》医院| 没姐妹很努力| 奔跑吧兄弟第十一季| 影视剧难以接受的结局| 战狼6欧美版免费资源下载 | 万古仙穹动漫第二季| 天梯粤语版| 白峰美羽在线视频| 羚羊号历险记| 爱情公寓5第13集| 传闻中的陈芊芊第一集| 小峓子4| 中国驻英领馆遭冲闯| 好男人在线电视剧在线观看免费高清 | 《龙之战》电影美国在线看| 特蕾西1987满天星| 女人电影| 本色电视剧免费版观看| 儿子与情人视频完整版在线观看| 动感之星妖精179集高清免费观看| 影帝的隐婚妻短剧全集| 亚伯风尘| 电影爱的色放| 漂亮的女朋友在线观看免费播放大全电视剧 | 小密逃| 电影神奇侠侣| 极度兽性在线观看| 最新恐怖电影2025在线观看| 八戒八戒电视剧观看22,辣妹子高清在线电视剧观看 | 回复术士之重启人生| 天罗地网 电影| 和部长一起出差的新婚妻子电影| 娘家故事之爱的重生| 温暖的村庄电视剧全集在线观看| 《最高の爱人诏》在哪儿可以看| 木马责罚| 烈火青春19| 义姐是不是良妈妈动漫第集在线看全| 《灰猎犬号》国语版| 老和尚与小姑娘的人物性格| 新金银梅5一10普通话| 小菊的春天电视剧全集完整版| 女儿 电影| 飞虎全集| 棒棒tv| 谍网迷魂| 火口的两个人去模糊板完整免费观看| 需要爸爸的种子| 性感的老师在线播放| 黛比浪漫女家教国语版哪里看 | 张艺谋新剧《雪迷宫》免费观看| 不扣纽扣的女人| 游樣2| 快乐到家电影完整版| 李妍雨短剧| 我爱男闺蜜迅雷下载| 《女销售员的销售秘密2》| 费马的料理| 1985版电影《干草堆》| 金银15美国欧式少妇16集全电视| 男女做爰猛烈叫床高潮的书| 放荡的女人2| 抗战40集免费观看| 播放郭德纲相声| 哇嘎全部高清在线观看| 昨日的果实| 狂飙 电视剧| 坎贝奇品味人生在线观看免费高清| 二次元动漫大片免费观看大全| 侠客行不通免费观看| 《牙医姐妹》完整版高清在线观看| 西域尸毒| 加勒比女海盗成版人2在线| 369你懂的电影| 性迷宫 电影 1999| 夏至未至豆瓣| 尼格买提复盘穿帮过程| 《妻子与游泳教练》主演| 末代福晋| 高铁坐过站可以免费坐回去| 柔情挚我电影| 销售的销售秘密6HD中字国| 密室大逃脱第七季免费观看完整版| 秋元まゆ花| 菠萝蜜菠萝蜜菠萝蜜免费观看| 韩 r| 电影《欲妇艳谭》| 太极生两仪 两仪是什么| 1982年禁忌3| 法国版女超人麦乐迪电影在线观 | 罗小黑战记38集免费观看| 电影牙医姐妹完整版高清在线观看 | 人猿泰山1995意大利哪里能看| 乡村爱情小夜曲片尾曲| 黑暗侵袭1高清| 背后的故事赵薇| 爱我几何莫妮卡电影无删减版叫什么| 让子弹飞 电影| 遍地狼烟在线观看| 七十二家房客第七季全集| 《四位少妇精油按摩》| 母爱无悔电视剧| 约会大作战第三季| 陈列之家| 男性按摩师按摩私处电影| 西环浮尸| 高清有罪之身| 奔跑吧第五季在线观看免费完整版| 战狼6全集免费下载| 佳恩别哭国语版| 决战次时代樱花动漫| 断背山百度影音| HD版《激战丛林》完整版播放_百度搜| 回复术士的重来人生樱花在线观看| 人民万岁电影| 为人民服务 高清| 卖保险套的女销售| 莉娜安德森AⅤ无删减版| 半把剪刀| 天网cctv| 电影《功夫》免费观看| 女职工的付出| 冤家亲家电视剧| 卿本佳人完整版| 美式家庭禁忌结局-3 | 《航空服务4》电影| 女婿回乡全集免费看| 孤注一掷在线观看高清免费张艺兴| 夫妻俩一起按摩精油电影| 变身小姐 豆瓣| 第七座墓志铭电视剧| 媛崽好困| 爱我几何意大利电影完整版播放| 莲花楼电视剧免费观看| 棋魂 电视剧 在线观看| 家族正名:恩怨了结全集免费| 铿锵锵锵锵锵免费观看电视剧| 隋唐英雄82| 吻到停不下来| 《维修工》李丹妮| 西班牙电视剧《爱的释放》| 沙漏全集| 聊斋艳谭之艳魔大战| 美国爸爸要播放的电影| 韩国 售楼小姐的秘密 在线 | 交换上司的秘书免费观看| 奇迹蓝罐| 歌手在线观看免费完整版| 冯小刚:《非3》带着笑声告别| 1995版新金银悔1-5普通话 | 拆局专家迅雷下载| 这么多年电影在线观看| 法国电影《私人助理》的导演是谁| 农民伯伯下乡2第二部叫什么名字 部长出差的日子 免费看 | 《男按摩师做乳房按摩》在线观看视 | 电影急诊护士| 聊斋艳谭五通神| 春风物语6| 凯·帕克《囚禁》| 束缚游戏免费全集在线看| 情色电影网| 国家元首未删减| 需要爸爸播种子-在线追剧| 僵尸100| 超级鉴定师| 我心里有危险的东西| 刚结婚陪部长出差的日子在线观看 | 山东妇女P大毛多水多| 薛姨妈hi版红楼梦免费观看| 长扇舞欢聚一堂| k8s经典版(老经典版)全集免费 | 牙医姐妹在线看完整版高清电影 | 全部视频排列表寂寞影院| 好日子在线观看免费完整版视频| 铁甲钢拳在线观看| 游戏| XL司令第一季全集免费完整版在线观看 | 隋唐英雄111| 农场主的女儿们kB经曲 | 反叛的鲁鲁修| 劳拉失魂记| 夺命追杀美国电影|