2017-08-08 1 views
4

私は、バイナリファイルを読み込むためにC言語で次のメソッドを使用していますが、それぞれのエラー値を確認する必要があり長時間となります。freadコール、これを対応して?複数のfreadコールからのエラーを処理するためのちょっとした方法

構造体を一度に読み込むことで呼び出しの一部を減らすことができますが、Cが構造体のメンバーの間にどのようにパディングバイトを追加できるかによって、これを避けることをお勧めします。

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) 

その後、READ_FIELDまたはとしてDO_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に初期化し、一箇所で集中管理する必要があります。 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; 
    } 

はOKですが、それは何もしない、したがって、あなたはsometingポインタがfreeを呼び出す前にNULLあるかどうかを確認する必要はありません。

サイドノート:Cでは、返される値はmallocではありません。

0

はここmallocfread操作がグループに簡単な方法で、適切な完了を一度だけチェック:free()にNULLポインタを渡すことはOKであることを

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; 
} 

注意。

関連する問題