質問

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

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

この配列の設定方法は、使用方法と同じであるという印象を受けました

char name[256];

両方の方法でfree()を使用する必要があること。私は間違っているのですか?そうであれば、誰かが低レベルの用語で違いを説明してください?

役に立ちましたか?

解決

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

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

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

補遺(Pax):

これにNedを追加すると、ほとんどの実装は通常、スタックよりも多くのヒープを提供します(少なくともデフォルトでは)。すでにスタックが不足しているか、非常に再帰的な処理を行っていない限り、これは通常256バイトでは問題になりません。

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

補遺の終了(Pax)。

他のヒント

  

両方の方法でfree()の使用が必要になること。

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

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

} /* SP -= 10 */

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

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

一部の「最近の」 Cの形式であるC99では、配列に実行時サイズを与えることができます。つまり、次のことが許可されています。

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

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

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

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

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

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

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

「宣言済み」全体を思い出すと便利です。 vs" defined"ここにあるもの。次に例を示します

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

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

ステートメントを破壊する

char* name = malloc(256*sizeof(char)); // one statement
char *name; // Step 1 declare pointer to character
name = malloc(256*sizeof(char)); // assign address to pointer of memory from heap
name[2]; // access 3rd item in array
*(name+2); // access 3rd item in array
name++; // move name to item 1

Translation:nameは、ヒープ上のメモリのアドレスに割り当てられる文字へのポインタになりました

char name[256]; // declare an array on the stack
name++; // error name is a constant pointer
*(name+2); // access 3rd item in array
name[2]; // access 3rd item in array
char *p = name;
p[2]; // access 3rd item in array
*(p+2); // access 3rd item in array
p++; // move p to item 1
p[0]; // item 1 in array

翻訳:名前は、スタック上のメモリを指す文字への定数ポインタです

Cでは、配列とポインターはほぼ同じものです。配列はメモリへの定数ポインタです。主な違いは、mallocを呼び出すと、ヒープからメモリを取得し、ヒープから取得したメモリをヒープから解放する必要があることです。サイズを指定して配列を宣言すると、スタックからメモリが割り当てられます。ヒープからメモリを解放するために解放が行われるため、このメモリを解放できません。スタックのメモリは、現在のプログラムユニットが戻ると自動的に解放されます。 2番目の例では、free(p)もエラーになります。 pは、スタック上の名前配列へのポインターです。したがって、pを解放することにより、スタック上のメモリを解放しようとしています。

これは次と違いはありません:

int n = 10;
int *p = &n;
この場合、pはスタック上の変数であるnを指すため、

pを解放するとエラーになります。したがって、pはスタック内のメモリ位置を保持し、解放できません。

int *p = (int *) malloc(sizeof(int));
*p = 10;
free(p);

この場合、pはmallocによって割り当てられたヒープ上のメモリ位置を指すため、空きは正しいです。

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

また、char []はほとんどの場合文字列に使用されるため、文字列構築メソッドが問題の文字列に必要なメモリを割り当てられるようにすることは悪い考えではなく、常に256(またはあなたが決めたどんな数字でも十分です。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top