49

動的に割り当てられた配列の長さを、それらを操作する関数に渡すのが一般的な規則であることを私は知っています。

void initializeAndFree(int* anArray, size_t length);

int main(){
    size_t arrayLength = 0;
    scanf("%d", &arrayLength);
    int* myArray = (int*)malloc(sizeof(int)*arrayLength);

    initializeAndFree(myArray, arrayLength);
}

void initializeAndFree(int* anArray, size_t length){
    int i = 0;
    for (i = 0; i < length; i++) {
        anArray[i] = 0;
    }
    free(anArray);
}

しかし、ポインターから割り当てられたメモリの長さを取得する方法がないfree()場合、与えているのがまったく同じポインターである場合、どのようにして割り当てを解除するかを「自動的に」知ることができますか? C プログラマーとして、なぜ魔法に乗れないのですか?

free()無料の (har-har) 知識はどこから得られますか?

4

9 に答える 9

33

標準では提供されていないという Klatchko の正しい点に加えて、実際の malloc/free 実装では、必要以上のスペースが割り当てられることがよくありますたとえば、12 バイトを要求すると、16 が提供される場合があります ( 16 が一般的なサイズであることに注意してください)。したがって、12 バイトを要求したことを知る必要はなく、16 バイトのチャンクが返されただけです。

于 2010-04-16T05:58:11.610 に答える
19

C委員会が標準でそれを要求しなかったので、あなたはそれを手に入れることができません.

移植性のないコードを書きたい場合は、次のような運があるかもしれません:

*((size_t *)ptr - 1)

または多分:

*((size_t *)ptr - 2)

ただし、それが機能するかどうかは、使用している malloc の実装がそのデータを格納する場所に正確に依存します。

于 2010-04-16T05:52:33.190 に答える
9

Klatchkoの回答を読んだ後、私自身がそれを試しptr[-1]、実際に実際のメモリを保存しました(通常、セグメンテーション違反に対して保存するために要求したメモリよりも多くなります)。

{
  char *a = malloc(1);
  printf("%u\n", ((size_t *)a)[-1]);   //prints 17
  free(a);
  exit(0);
}

さまざまなサイズで試してみると、GCCは次のようにメモリを割り当てます。

最初に割り当てられるメモリは17バイトです。
割り当てられたメモリは、要求されたサイズより少なくとも5バイト多く、さらに要求された場合は、8バイト多く割り当てられます。

  • サイズが[0,12]の場合、割り当てられるメモリは17です。
  • サイズが[13]の場合、割り当てられるメモリは25です。
  • サイズが[20]の場合、割り当てられるメモリは25です。
  • サイズが[21]の場合、割り当てられるメモリは33です。
于 2010-04-16T11:14:08.280 に答える
9

メモリ アロケーターが割り当てられたブロックの前に配置するメタデータを取得することは可能ですが、これは、ポインターが実際に動的に割り当てられたブロックへのポインターである場合にのみ機能します。これは、渡されたすべての引数が単純な auto または static 配列ではなく、そのようなブロックへのポインターである必要がある関数のユーティリティに深刻な影響を与えます。

ポイントは、ポインターの検査から、それが指しているメモリのタイプを知るための移植可能な方法がないことです。したがって、これは興味深いアイデアですが、特に安全な提案ではありません。

安全で移植可能な方法は、長さを保持するために割り当ての最初のワードを予約することです。GCC (およびおそらく他のいくつかのコンパイラ) は、長さゼロの配列を持つ構造体を使用してこれを実装する移植性のない方法をサポートしています。これにより、移植性のあるソリューションと比較してコードが多少簡素化されます。

typedef struct
{
    size_t length ;
    char alloc[0] ;   // Compiler specific extension!!!
} tSizedAlloc ;

// Allocating a sized block
tSizedAlloc* blk = malloc( sizeof(tSizedAlloc) + length ) ;
blk->length = length ;

// Accessing the size and data information of the block
size_t blk_length = blk->length ;
char*  data = blk->alloc ;
于 2010-04-16T08:27:54.593 に答える
4

このスレッドが少し古いことは承知していますが、それでも言いたいことがあります。関数 (またはマクロ、まだライブラリをチェックしていません) があります malloc_usable_size() - ヒープから割り当てられたメモリ ブロックのサイズを取得します。マニュアルページには、デバッグ専用であると記載されています。これは、要求した数ではなく、割り当てられた数を出力するためです。これは少し大きいです。これは GNU 拡張であることに注意してください。

一方、メモリチャンクを解放するためにそのサイズを知る必要はないと私は信じているので、それは必要でさえないかもしれません。チャンクを担当するハンドル/記述子/構造を削除するだけです。

于 2012-10-24T14:44:49.390 に答える
3

非標準的な方法は、 を使用すること_msize()です。この関数を使用すると、コードが移植できなくなります。malloc()また、ドキュメンテーションは、渡された数値を返すか、実際のブロックサイズを返すかについて明確ではありません(より大きな可能性があります)。

于 2010-04-16T06:15:59.377 に答える
2

このデータをどのように格納するかは、malloc実装者次第です。ほとんどの場合、長さは割り当てられたメモリの直前に格納されます (つまり、7 バイトを割り当てたい場合、実際には 7+x バイトが割り当てられ、x 追加バイトがメタデータの格納に使用されます)。ヒープの破損をチェックするために、割り当てられたメモリの前後にメタデータが格納される場合があります。ただし、実装者は、追加のデータ構造を使用してメタデータを格納することも選択できます。

于 2010-04-16T06:01:00.627 に答える
1

サイズを保存するために、より多くのメモリを割り当てることができます。

void my_malloc(size_t n,size_t size ) 
{
    void *p = malloc( (n * size) + sizeof(size_t) );
    if( p == NULL ) return NULL;
    *( (size_t*)p) = n;
    return (char*)p + sizeof(size_t);
}

void my_free(void *p)
{
    free( (char*)p - sizeof(size_t) );
}

void my_realloc(void *oldp,size_t new_size)
{
    // ...
}

int main(void)
{
    char *p = my_malloc( 20, 1 );
    printf("%lu\n",(long int) ((size_t*)p)[-1] );
    return 0;
}
于 2010-06-01T13:39:27.120 に答える
0

delete []に​​関する質問に答えるために、C ++の初期のバージョンでは、実際にはdelete [n]を呼び出してランタイムにサイズを通知する必要があったため、保存する必要はありませんでした。残念ながら、この動作は「混乱しすぎる」として削除されました。

(詳細については、D&Eを参照してください。)

于 2011-01-01T19:41:28.167 に答える