2012-10-10 71 views
3

假設我有一個功能來執行一個相當不錯的可能性很小的特定任務。處理出錯的最佳方法是什麼? (假設我知道問題是什麼)。處理函數錯誤消息最理想的方法是什麼?

例如可以說我有一個功能,讀取兩個字節的字符串並返回它:

#include <stdio.h> 
#include <stdlib.h> 

char *bar(void) 
{ 
    char *foo = malloc(3); 
    scanf("%2s", foo); 
    return foo; 
} 

int main(void) 
{ 
    char *foo = bar(); 
    puts(foo); 
    free(foo); 
    return 0; 
} 

上面的例子絕對沒有錯誤處理任何責任。有兩種方法可以實現某種錯誤處理,但我不確定哪種方法更受歡迎或被認爲是最佳實踐。

方法1(印刷錯誤消息要從函數內標準錯誤):

#include <stdio.h> 
#include <stdlib.h> 

char *bar(void) 
{ 
    char *foo; 
    if(!(foo = malloc(3))) 
    { 
     fputs("\nError! Memory allocation failed.", stderr); 
     return 0x00; 
    } 
    scanf("%2s", foo); 
    return foo; 
} 

int main(void) 
{ 
    char *foo; 
    if(!(foo = bar())) return 1; 
    puts(foo); 
    free(foo); 
    return 0; 
} 

方法2(印刷錯誤消息從調用函數到stderr):

#include <stdio.h> 
#include <stdlib.h> 

char *bar(void) 
{ 
    char *foo; 
    if(!(foo = malloc(3))) return 0x00; 
    scanf("%2s", foo); 
    return foo; 
} 

int main(void) 
{ 
    char *foo; 
    if(!(foo = bar())) 
    { 
     fputs("\nError! Memory allocation failed.", stderr); 
     return 1; 
    } 
    puts(foo); 
    free(foo); 
    return 0; 
} 

我幾乎認爲方法二將是最好的方式去,因爲這樣我可以得到更具體的我的錯誤消息,這取決於我當時調用該函數。 我對第二種方法的擔憂是,如果它有多個潛在的失敗點,我就無法打印出在函數中出錯的地方。

僞代碼:

IF FAILUREA 
    PRINT "FAILUREA OCCURED" 
    RETURN 
IF FAILUREB 
    PRINT "FAILUREB OCCURED" 
    RETURN 

這不會是太大的問題,如果我是調用函數是一個int,因爲這樣我可以只返回基於什麼地方出了錯不同的整數值。但在char*的情況下,我通常會在失敗時嘗試返回NULL(因此FAILUREAFAILUREB將返回NULL);將無法知道是什麼導致該功能失敗。

所以我的問題是什麼是處理錯誤消息的最佳做法?

+1

'if(!(foo = malloc(3)))return 0;' – wildplasser

+0

只是爲了便於閱讀? –

+0

不,在bar()函數中,就在「Method2」下的普通泄漏malloc()中。 – wildplasser

回答

2

允許調用來處理錯誤報告是更好,因爲:

  • 函數是否形成庫stderr的一部分可能無法使用,並且需要一個替代的報告機制。
  • 調用代碼可能會採取替代措施,並且可能不會將功能bar()的故障視爲實際故障,因此無需報告。

如果一個函數有多個可能的失敗原因,那麼可能會傳遞一個參數給在發生失敗時更新的函數。調用函數可以根據實際失敗原因選擇適當的操作。例如:

enum Status 
{ 
    STATUS_OK, 
    STATUS_MEMORY_ALLOCATION_FAILURE, 
    STATUS_ACCESS_DENIED 
}; 

enum Status status; 
char* foo = bar(&status); 
if (!foo) 
{ 
    if (STATUS_MEMORY_ALLOCATION_FAILURE == status) 
    { 
     /* report failure. */ 
    } 
    else if (STATUS_ACCESS_DENIED == status) 
    { 
     /* try somewhere else */ 
    } 
} 
+0

太棒了,正是我在找的東西。也很簡單,重要。謝謝您的回答! –

1

可以這樣做,如果你的函數返回比1的錯誤情況

#include <stdio.h> 
#include <stdlib.h> 

int bar(char **foo) 
{ 
    if(!(malloc(3))) return 1; /* return error case 1*/ 
    scanf("%2s", *foo); 
    if(!(malloc(4))) return 2; /* return error case 2*/ 
    return 0; /* no error*/ 
} 

int catcherror(int error) 
{ 
    switch (error) { 
     case 1: 
      /*do something 1*/ 
     case 2: 
      /*do something 1*/ 
     case 3: 
      /*do something 1*/ 
     case 4: 
      /*do something 1*/ 
     case 5: 
      /*do something 1*/ 
     default: 
      /*do something 1*/ 
    } 
} 

int main(void) 
{ 
    char *foo; 
    int error 

    error = bar(&foo); 
    catcherror(error); 
    puts(foo); 
    free(foo); 
    return 0; 
} 

catcherror()功能可能是非常有用的多,如果你的項目包含返回一個常見的錯誤很多功能案例

2

如果你可以做任何關於失敗的事情,如果你打算去做,那麼你就做到了。 否則,你可以實現一個通用的失敗函數,調用它在錯誤的情況下,並收工:

void error(const char* format, ...) 
{ 
    va_list vl; 
    va_start(vl, format); 
    vfprintf(stderr, format, vl); 
    va_end(vl); 
    exit(-1); 
} 

您可以選擇將它包裝在一個宏用行#和文件名提供它:

#define ERROR(fmt, ...) \ 
    error("file:'%s',line:%d " fmt, __FILE__, __LINE__, __VA_ARGS__) 

這將使控制檯中的錯誤非常容易找出,因爲錯誤消息會精確地告訴文件和錯誤發生處的行。

典型用法,沒有任何幻想:

char *bar(void) 
{ 
    char *foo; 
    if ((foo=malloc(3)) == NULL) 
    ERROR("malloc() failed!\n"); 
    if (scanf("%2s", foo) != 1) 
    ERROR("scanf() failed!\n"); 
    return foo; 
} 

您可以使用longjmp()代替exit(-1)立即返回給調用者(=就是那個做了相應的setjmp())如果你想真正做一些事情於錯誤,也許關閉所有文件以便寫入,所以緩衝的數據不會丟失。

例如,如果您正在編寫一個簡單的編譯器,那麼這種類型的編譯器內部的大多數錯誤以及正在編譯的源代碼中的問題已經足夠了(例如,缺少冒號/ paren或其他內容這使得代碼不可編譯)。

如果你不能或不想做任何這樣的事情,你需要仔細編寫代碼,做適當的清理並返回不同的錯誤代碼來向調用者傳遞可操作的錯誤。

相關問題