13

ここの誰かが最近、私が使用している私のコードの一部で私に指摘しました

char* name = malloc(256*sizeof(char));
// more code
free(name);

このアレイのセットアップ方法は、

char name[256];

どちらの方法でも free() を使用する必要があります。私は間違っていますか?もしそうなら、誰かが低レベルの用語で違いを説明してもらえますか?

4

6 に答える 6

29

最初のコードでは、メモリが動的にヒープに割り当てられます。そのメモリは free() で解放する必要があります。その有効期間は任意です。関数の境界を越えることもできます。

2 番目のコードでは、256 バイトがスタックに割り当てられ、関数が戻るとき (またはすべての関数の外にある場合はプログラムの終了時) に自動的に回収されます。そのため、 free() を呼び出す必要はありません (また、呼び出すこともできません)。リークすることはありませんが、関数の終わりを超えて存続することもありません。

メモリの要件に基づいて、2 つのいずれかを選択します。

補遺 (パックス):

これに付け加えるとすれば、ネッド、ほとんどの実装は通常、スタックよりも多くのヒープを提供します (少なくともデフォルトでは)。これは通常、256 バイトでは問題になりません。ただし、既にスタックを使い果たしている場合や再帰的な処理を頻繁に行っている場合を除きます。

また、 sizeof(char) は標準に従って常に 1 であるため、余分な乗算は必要ありません。コンパイラーはおそらくそれを最適化して取り除きますが、それはコードを醜い IMNSHO にします。

補遺 (Pax) を終了します。

于 2009-01-07T02:48:14.687 に答える
7

どちらの方法でも free() を使用する必要があります。

いいえ、無料の使用が必要なのは最初だけです。2 番目はスタックに割り当てられます。これにより、割り当てが非常に高速になります。ここを見て:

void doit() {
    /* ... */
    /* SP += 10 * sizeof(int) */
    int a[10];
    /* ... (using a) */

} /* SP -= 10 */

作成すると、コンパイラはコンパイル時にそのサイズを認識し、スタックに適切なサイズを割り当てます。スタックは、どこかにある継続的なメモリの大きな塊です。スタックに何かを置くと、スタックポインタがインクリメント (プラットフォームによってはデクリメント) されます。スコープ外に出ると逆になり、配列が解放されます。それは自動的に起こります。そのため、そのように作成された変数には、自動保存期間があります。

malloc の使用は異なります。( freestoreと呼ばれる場所から) 任意の大きなメモリ チャンクを注文します。ランタイムは、かなり大きなメモリ ブロックをルックアップする必要があります。サイズは実行時に決定できるため、通常、コンパイラはコンパイル時にサイズを最適化できません。ポインタはスコープ外に出たり、コピーされたりする可能性があるため、割り当てられたメモリとメモリ アドレスが割り当てられたポインタとの間に固有の結合はありません。 . そうする時が来たら、手動で malloc から取得したアドレスを渡して free を呼び出す必要があります。

C99 と呼ばれる「最近の」形式の C では、配列にランタイム サイズを指定できます。つまり、次のことが許可されています。

void doit(int n) {
    int a[n]; // allocate n * sizeof(int) size on the stack */
}

ただし、使用する理由がない場合は、その機能を使用しない方がよいでしょう。理由の 1 つは、フェイルセーフではないことです。使用可能なメモリがなくなった場合、何かが起こる可能性があります。もう 1 つの理由は、C99 はコンパイラ間での移植性があまり高くないことです。

于 2009-01-07T02:52:39.187 に答える
6

ここで 3 番目の可能性があります。配列は関数の外部で宣言できますが、静的に宣言できます。たとえば、

// file foo.c
char name[256];

int foo() {
    // do something here.
}

SOに関する別の質問への回答に、誰かがこれがCでは不適切だと感じたということにかなり驚きました。ここでは言及すらされておらず、私はこれについて少し混乱して驚いています (「最近、学校で子供たちに何を教えているのですか?」など)。

この定義を使用すると、メモリはヒープでもスタックでもなく、イメージ内のデータ空間で静的に割り当てられます。したがって、malloc/free のように管理する必要はなく、自動定義の場合のようにアドレスが再利用されることを心配する必要もありません。

ここで「宣言された」対「定義された」こと全体を思い出すと便利です。これが例です

/* example.c */

char buf1[256] ;           /* declared extern, defined in data space */
static char buf2[256] ;    /* declared static, defined in data space */
char * buf3 ;              /* declared extern, defined one ptr in data space */
int example(int c) {       /* c declared here, defined on stack */
    char buf4[256] ;       /* declared here, defined on stack   */
    char * buf5 = malloc(256)]   /* pointer declared here, defined on stack */
                           /* and buf4 is address of 256 bytes alloc'd on heap */
    buf3 = malloc(256);    /* now buf3 contains address of 256 more bytes on heap */

    return 0;              /* stack unwound; buf4 and buf5 lost.      */
                           /* NOTICE buf4 memory on heap still allocated */
                           /* so this leaks 256 bytes of memory */
}

今はまったく別のファイルに

/* example2.c */

extern char buf1[];             /* gets the SAME chunk of memory as from example.c */
static char buf2[256];          /* DIFFERENT 256 char buffer than example.c */
extern char * buf3 ;            /* Same pointer as from example.c */
void undoes() {
     free(buf3);                /* this will work as long as example() called first */
     return ;
}
于 2009-01-07T04:07:28.473 に答える
2

これは正しくありません。配列の宣言に free は必要ありません。さらに、これが関数内にある場合は、スタックに割り当てられ (メモリが機能する場合)、関数が戻ると自動的に解放されます。呼び出し元に参照を渡さないでください。

于 2009-01-07T02:46:44.177 に答える
0

これを実行している場所によっては、スタックスペースが非常に貴重になる場合があります。たとえば、Verizon/Alltel ハンドセット用の BREW コードを作成している場合、通常、ごくわずかなスタックに制限されますが、ヒープ アクセスは増え続けます。

また、char[] は文字列に最も頻繁に使用されるため、文字列構築メソッドが問題の文字列に必要なメモリを割り当てられるようにすることは悪い考えではありません。令)で十分です。

于 2009-02-11T20:11:36.220 に答える