2017-08-08 139 views
4

我有以下方法在C加載一個二進制文件,它似乎相當長和乏味的檢查每個fread調用的錯誤值,有沒有一個更好的方法來處理這個(事情?我知道可以通過一次性讀取結構來減少一些調用,但由於C可以如何在結構成員之間添加填充字節,我寧願避免這種情況。Neater的方式來處理來自多個fread調用的錯誤

some_type_t *load_something(FILE *file) { 
    some_type_t *something = (some_type_t *)malloc(sizeof(some_type_t)); 
    if (something == NULL) { 
     return NULL; 
    } 

    if (fread(&something->field1, sizeof(something->field1), 1, file) == 0) { 
     free(something); 
     return NULL; 
    } 
    if (fread(&something->field2, sizeof(something->field2), 1, file) == 0) { 
     free(something); 
     return NULL; 
    } 
    if (fread(&something->field3, sizeof(something->field3), 1, file) == 0) { 
     free(something); 
     return NULL; 
    } 

    uint16_t some_var1, some_var2, some_var3; 

    some_other_type_t *something_else1 = (some_other_type_t *)malloc(sizeof(some_other_type_t)); 
    if (fread(&some_var1, sizeof(some_var1), 1, file) == 0) { 
     free(something); 
     free(something_else1); 
     return NULL; 
    } 

    some_other_type_t *something_else2 = (some_other_type_t *)malloc(sizeof(some_other_type_t)); 
    if (fread(&some_var2, sizeof(some_var2), 1, file) == 0) { 
     free(something); 
     free(something_else1); 
     free(something_else2); 
     return NULL; 
    } 

    some_other_type_t *something_else3 = (some_other_type_t *)malloc(sizeof(some_other_type_t)); 
    if (fread(&some_var3, sizeof(some_var3), 1, file) == 0) { 
     free(something); 
     free(something_else1); 
     free(something_else2); 
     free(something_else3); 
     return NULL; 
    } 
    // Do something with the vars and allocated something elses. 
    // ... 

    return something; 
} 

回答

2

爲什麼不創建宏:

#define READ_FIELD(data) \ 
    do { if (fread(&data, sizeof(data), 1, file) == 0) { \ 
     free(something); \ 
     free(something_else1); 
     free(something_else2); 
     return NULL; \ 
    } } while(0) 

然後調用它像一個函數調用:

READ_FIELD(something->field1); 
READ_FIELD(something->field2); 

READ_FIELD(some_var1); 
READ_FIELD(some_var2); 

代碼將是相同的,但至少它現在產生,而不是複製/粘貼(可能有錯誤)。

宏必須在所有可能的內存塊上調用free,即使是尚未分配的內存塊。唯一的限制是將未分配的設置爲NULL,因此free不會崩潰。而且是超級安全的變化:

free(something); something = NULL; 

(當然如果something是分配的指針的副本設置爲NULL不能防止雙重自由,它有限制)

你可以申請這種技術來寫一邊爲好,由於M Oehm建議,你可以讓你想讀在包裝程序宏/寫什麼列表:

#define DO_ALL \ 
    DO_FIELD(something->field1); \ 
    DO_FIELD(something->field2); \ 
    DO_FIELD(some_var1); \ 
    DO_FIELD(some_var2) 

然後定義DO_FIELDREAD_FIELDWRITE_FIELD

#define DO_FIELD READ_FIELD 
DO_ALL; 
#undef DO_FIELD 
+0

看起來很有吸引力,但還有一個額外的複雜因素,我越讀越多,我可能需要釋放的東西就越多。我會更新這個問題來反映這一點。 – DanielGibbs

+0

你可以釋放所有東西,即使事情還沒有分配,只要指針是NULL,它不會是一個問題。 –

+0

你甚至可以使用包裝宏來保持寫入和讀取同步。 –

2

沒有什麼能爲您節省檢查每個調用的成功的,但你可以在C使用goto代碼結構改善(其實這是一個習慣用法的goto,僞代碼如下):

if (first_call() < 0) goto error; 
    if (second_call() < 0) goto error; 

    // [...] 
    // when everything succeeded: 
    return result; 

error: 
    // free resources 
    // return error-indicator, e.g. 
    return 0; 

如果資源到了你的函數的過程中自由積累,首先要確保他們所有的初始化爲NULL/0(假設他們是指針)。然後,在error部分上的free()對它們沒有任何影響。如果你使用自己的「析構函數」,確保按照free()的設計方式進行設計 - 當傳遞NULL值時,它應該是無操作的。

1

你需要的所有something指針初始化爲NULL,並在一個地方集中進行清理。

... 
    something = something_else1 = something_else2 = something_else3 = NULL; 
    ... 
    some_other_type_t *something_else3 = (some_other_type_t *)malloc(sizeof(some_other_type_t)); 
    if (fread(&some_var3, sizeof(some_var3), 1, file) == 0) { 
     goto error; 
    } 
    // Do something with the vars and allocated something elses. 
    // ... 

    return something; 

    error: 
    free(something); 
    free(something_else1); 
    free(something_else2); 
    free(something_else3); 
    return NULL; 
    } 

解放出來一個NULL指針就OK了,它什麼都不做,所以你不需要檢查someting指針調用free之前NULL

旁註:在C中,您不會投出返回值malloc

0

這裏有一個簡單的方法來組mallocfread操作和正確完成檢查只是一次:

some_type_t *load_something(FILE *file) { 
    uint16_t some_var1, some_var2, some_var3; 
    some_type_t *something = malloc(sizeof(*something)); 
    some_other_type_t *something_else1 = malloc(sizeof(*something_else1)); 
    some_other_type_t *something_else2 = malloc(sizeof(*something_else2)); 
    some_other_type_t *something_else3 = malloc(sizeof(*something_else3)); 

    if (!something || !something_else1 || !something_else2 || !something_else3 || 
     !fread(&something->field1, sizeof(something->field1), 1, file) || 
     !fread(&something->field2, sizeof(something->field2), 1, file) || 
     !fread(&something->field3, sizeof(something->field3), 1, file) || 
     !fread(&some_var1, sizeof(some_var1), 1, file) || 
     !fread(&some_var2, sizeof(some_var2), 1, file) || 
     !fread(&some_var3, sizeof(some_var3), 1, file)) 
    { 
     free(something); 
     free(something_else1); 
     free(something_else2); 
     free(something_else3); 
     return NULL; 
    } 

    // Do something with the vars and allocated something elses. 
    // ... 

    return something; 
} 

注意,傳遞一個空指針free()是OK。