2011-09-20 12 views
5

:BASE_ADDRESSへの最初の呼び出しが成功し、asprintfへの2回目の呼び出しは()が失敗した場合は?このような関数宣言を考える

int base_address(zval *object, int add_prefix, char **base_address TSRMLS_DC) {  
    int result; 

    char *host; 
    long port; 
    char *prefix; 

    host = ... get host from object ...; 
    port = ... get port from object ...; 
    prefix = ... get prefix from object ...; 

    result = SUCCESS; 

    if (asprintf(base_address, "%s:%ld/%s", host, port, prefix) < 0) { 
     result = FAILURE; 
    } 

    return result; 
} 

void my_func() { 
    char *base_address; 
    char *ping_url; 

    if (base_address(getThis(), 0, &base_address TSRMLS_CC) == FAILURE) { 
     MALLOC_ERROR(); 
    } 

    if (asprintf(&ping_url, "%s/ping", base_address) < 0) { 
     MALLOC_ERROR(); 
    } 

    ... do some stuff with base address ... 

    // release both, as everything worked 
    free(base_address); 
    free(ping_url); 
} 

を、どのように私はきれいに安全に割り当てられたを解放するために、関数の最後までスキップしますメモリ?

メモリが次々に割り当てられている(各割り当てが失敗する場合があります)あまりにも多くのコードの重複や後藤文なしにこのような状況でメモリリークを回避するための方法をいくつかの標準的なパターンはありますか?

+5

後藤は後藤と間違って何ここ –

+0

あなたの友達ですか? do while(0)構文を使うことができますが、それは基本的に同じことです。 – Luke

+0

RAIIはどうですか? – Dani

答えて

2

そしてfreeは、彼らが同様にmallocエドことはありませんでしたケースを扱うことができるようになります(それは単に意志何もしない)。 (例えば、-1を用いることができる他の警備員は、例えば、無効FDを参照する。)

を私は(追加)のクリーンアップのために使用gotoとと一緒にこのを使用する - 他の回答の一部私には "言葉"を遠く見てください。私はgotoが賢明にスパゲッティコードを避けるために使用されなければならないことを知り、一般に、「gotosの周りにあるgotos」は一貫して追跡するのが非常に困難です。 NULLの追加の割り当ては最初にも、変数自体がチェックガード(任意の可能なクリーンアップ中又はその他)として使用されることを可能にします。

ハッピーコーディング。


例:

void my_func() { 
    char *base_address = NULL; 
    char *ping_url = NULL; 

    if (base_address(getThis(), 0, &base_address TSRMLS_CC) == FAILURE) { 
     goto cleanup; 
    } 

    if (asprintf(&ping_url, "%s/ping", base_address) < 0) { 
     goto cleanup; 
    } 

    // stuff... and assign return value (or return directly) if applicable, 
    // assign NULL to variables which contain values that should 
    // not be free'd, etc (use as guards!). 
    // I prefer to add guards into the cleanup vs. "skip over it". 
    // I vary rarely, if ever, have multiple cleanup sections -- in most 
    // cases this would indicate the need for additional function(s) to me. 

    cleanup: 
    free(base_address); 
    if (ping_url) { 
     // perhaps need additional cleanup   
     free(ping_url); 
    } 

    // Return value, if applicable. (If a "direct return" above isn't 
    // advisable for the given function due to cleanup rules.) 
} 
2

Cでは、あなたはかなりあなたのケースで

ハンドリングエラーを処理するために)(場合の対処するために残されている

if (base_address(...)) 
{ 
    if (asprint(...)) 
    { 
    /// do some stuff 

    free(ping_url); 
    } 
    free(base_address); 
} 
7

これは、エラー処理のためのgotoの健全な使用法の一つであります:

if (base_address(getThis(), 0, &base_address TSRMLS_CC) == FAILURE) { 
    goto end; 
} 

if (asprintf(&ping_url, "%s/ping", base_address) < 0) { 
    goto release_address; 
} 

// do stuff 

release_address: 
free(base_address); 
end: 

あなたがお互いに依存して、多くの割り当てのコールを持っている場合には、同じリリースコードを繰り返す必要はありません。この方法では。

あなたは一般的なケースについて語る私の答えhere、別のを参照することもできます。

+2

もし誰かがgotoのためにこれを投票したら、彼は直接プログラミングに何かをすることができます。 –

9

gotoを恐れてはいけません。

  • あなたは自分自身を繰り返さないでください。これは、Cで例外を処理する最もシンプルでクリーンで読みやすい方法です。重複したコードはエラーが発生しやすくなります。

  • あなたは深くネストされたコードを作成しないでください。深いネストは判読不能です。

  • do {...} while (0)breakの後ろに隠れません。良いコードは、それが意味することを言います。ここで

は、基本的な例です:NULLが宣言時にポインタ変数に割り当てられている場合

int operation() { 

    int result = SUCCESS; 

    if ((result = may_fail_first()) == FAILURE) { 
     goto failed_first; 
    } 

    if ((result = may_fail_second()) == FAILURE) { 
     goto failed_second; 
    } 

    // If your cleanup code doesn't ordinarily need to run. 
    goto end; 

failed_second: 
    cleanup_second(); 

    // If you don't need to clean up everything. 
    goto end; 

failed_first: 
    cleanup_first(); 

end: 
    return result; 

}