web-dev-qa-db-ja.com

Cで動的に割り当てられたメモリのサイズを決定する

Cで動的に割り当てられたメモリのサイズを調べる方法はありますか?

たとえば、

char* p = malloc (100);

pに関連付けられているメモリのサイズを調べる方法はありますか?

54
s_itbhu

comp.lang.c FAQリスト・質問7.27 -

Q.では、mallocパッケージを照会して、割り当てられたブロックの大きさを確認できますか?

A.残念ながら、標準的な方法や移植可能な方法はありません。 (一部のコンパイラは非標準の拡張機能を提供します。)知る必要がある場合は、自分で追跡する必要があります。 (質問 7.28 も参照してください。)

43
Alex Reynolds

この情報を見つける標準的な方法はありません。ただし、一部の実装では、これを行うためにmsizeなどの関数が提供されます。例えば:

ただし、mallocは要求されたサイズの最小値を割り当てるため、実装のmsizeバリアントが実際にオブジェクトのサイズを返すか、ヒープに実際に割り当てられたメモリを返すかを確認する必要があります。

43
ars

Cの考え方は、プログラマーに仕事を支援するツールを提供することであり、仕事の性質を変える抽象化を提供することではありません。 Cは、パフォーマンスの制限を犠牲にしてこれが発生した場合、物事をより簡単/安全にすることも避けようとします。

メモリの領域で実行したい特定のことは、領域の開始位置のみを必要とします。そのようなことには、ヌル終了文字列の操作、領域の最初のnバイトの操作(領域が少なくともこの大きさであることがわかっている場合)などが含まれます。

基本的に、領域の長さを追跡することは余分な作業であり、Cが自動的に実行した場合、不必要に実行されることがあります。

多くのライブラリ関数(たとえば、fread())は、領域の開始点へのポインターと、この領域のサイズを必要とします。領域のサイズが必要な場合は、それを追跡する必要があります。

はい、malloc()実装は通常、領域のサイズを追跡しますが、間接的にこれを行うか、ある値に切り上げるか、まったく保持しない場合があります。彼らがそれをサポートしていても、この方法でサイズを見つけるのは、自分でそれを追跡するのに比べて遅いかもしれません。

各地域の大きさを知っているデータ構造が必要な場合は、Cがそれを行います。領域の大きさと領域へのポインタを追跡する構造体を使用するだけです。

11
Artelius

いいえ、Cランタイムライブラリはそのような機能を提供しません。

一部のライブラリは、この情報を取得できるプラットフォーム固有またはコンパイラ固有の関数を提供する場合がありますが、通常、この情報を追跡する方法は別の整数変数です。

7
Greg Hewgill

これは、サイズをアドレスとともに保存するためのタグ付きポインターを作成するために私が見た最良の方法です。すべてのポインター関数は引き続き期待どおりに機能します。

盗まれた: https://stackoverflow.com/a/35326444/638848

また、mallocのラッパーを実装し、mallocが返すポインターの前にタグ(割り当てられたサイズやその他のメタ情報など)を自由に追加することもできます。これは、実際には、c ++コンパイラがオブジェクトに仮想クラスへの参照をタグ付けする方法です。作業例の1つを次に示します。

#include <stdlib.h>
#include <stdio.h>

void * my_malloc(size_t s) 
{
  size_t * ret = malloc(sizeof(size_t) + s);
  *ret = s;
  return &ret[1];
}

void my_free(void * ptr) 
{
  free( (size_t*)ptr - 1);
}

size_t allocated_size(void * ptr) 
{
  return ((size_t*)ptr)[-1];
}

int main(int argc, const char ** argv) {
  int * array = my_malloc(sizeof(int) * 3);
  printf("%u\n", allocated_size(array));
  my_free(array);
  return 0;
}

サイズとポインターのある構造に対するこのメソッドの利点

 struct pointer
 {
   size_t size;
   void *p;
 };

malloc呼び出しとfree呼び出しを置き換えるだけでよいということです。他のすべてのポインター操作は、リファクタリングを必要としません。

5
Leslie Godwin

他の皆がすでに言ったように:いいえ、ありません。

また、ベンダー固有の機能はすべてここでは常に回避します。実際に使用する必要があることがわかった場合、それは一般的に間違っていることを示しているからです。サイズを個別に保存するか、まったく知らなくてもかまいません。ベンダー関数を使用することは、Cで記述する主な利点の1つである移植性を失う最も簡単な方法です。

3
Enno

これは実装に依存すると予想されます。
ヘッダーデータ構造を取得した場合は、ポインターにキャストしてサイズを取得できます。

2
nik

さて、これはあなたの特定の質問に答えているわけではないことを知っていますが、箱の外で考えていたのは... OK、OK、いいえ、あなたは悪いまたは非正統的な実装を持っているわけではありません...私はあなたがおそらくあなたのデータを適合できるかどうかだけを知りたいと思うでしょう割り当てられたメモリで、その場合はこのソリューションの方が良いかもしれません。それがあまりにも多くのオーバーヘッドを提供してはならず、それが本当にあなたが扱っているものであるなら、あなたの「適合」問題を解決します:

if ( p != (tmp = realloc(p, required_size)) ) p = tmp;

または、古いコンテンツを維持する必要がある場合:

if ( p != (tmp = realloc(p, required_size)) ) memcpy(tmp, p = tmp, required_size);

もちろん、あなただけを使用することができます:

p = realloc(p, required_size);

それで終わりです。

1
Erich Horn

このコードは、ほとんどのWindowsインストールでおそらく動作します。

template <class T>
int get_allocated_bytes(T* ptr)
{
 return *((int*)ptr-4);
}

template <class T>
int get_allocated_elements(T* ptr)
{
 return get_allocated_bytes(ptr)/sizeof(T);
}
1
H. Acker

Mallocを使用すると、サイズを取得できません。

一方、Windows ヒープ関数 のようにOS APIを使用してメモリを動的に割り当てる場合、それを行うことができます。

1

不可能だと言う人は皆、技術的に正しい(最高の正しい)。

エンジニアリング上の理由から、割り当てられたブロックのサイズを正確に伝えるためにmallocサブシステムに依存するのは悪い考えです。このことを納得させるために、いくつかの異なるメモリアロケータを使用してlargeアプリケーションを作成していることを想像してください。1つの部分で生のlibc mallocを使用し、別の部分でC++ operator newさらに別の部分にある特定のWindows API。あらゆる種類のvoid*が飛び回っています。これらのvoid*sのanyで機能する関数を作成することは、ポインターの値からヒープのどの部分から来たかを何らかの形で知ることができない限り不可能です。

したがって、プログラム内の各ポインターを、ポインターがどこから来たのか(そしてどこに戻す必要があるのか​​)を示す何らかの規則でラップしたい場合があります。たとえば、C++では、std::unique_ptr<void>operator delete 'dにする必要があるポインターの場合)またはstd::unique_ptr<void, D>(他の何らかのメカニズムDを介して返す必要があるポインターの場合)を呼び出します。必要に応じて、Cでも同じようなことができます。そして、一度より大きな安全なオブジェクトにポインタをラップするととにかくになり、struct SizedPtr { void *ptr; size_t size; }への小さなステップになり、割り当てのサイズを再度心配する必要がなくなります。

ただし、

also割り当ての実際の基礎サイズを合法的に知りたい理由があります。たとえば、プログラマ向けのメモリ量だけでなく、各サブシステムごとの実際のメモリ量sedをレポートするプロファイリングツールを作成しているかもしれませんthought =彼は使用していました。 10バイトの各割り当てが内部で16バイトを密かに使用している場合は、知っておくとよいでしょう! (もちろん、この方法で測定していない他のオーバーヘッドもあります。しかし、that job用のツールはまだあります。)または、単にreallocの動作を調査しているだけかもしれません。プラットフォーム。または、将来のpremature再割り当てを回避するために、増加する割り当ての容量を「切り上げ」たいと思うかもしれません。例:

SizedPtr round_up(void *p) {
    size_t sz = portable_ish_malloced_size(p);
    void *q = realloc(p, sz);  // for sanitizer-cleanliness
    assert(q != NULL && portable_ish_malloced_size(q) == sz);
    return (SizedPtr){q, sz};
}
bool reserve(VectorOfChar *v, size_t newcap) {
    if (v->sizedptr.size >= newcap) return true;
    char *newdata = realloc(v->sizedptr.ptr, newcap);
    if (newdata == NULL) return false;
    v->sizedptr = round_up(newdata);
    return true;
}

Null以外のポインターの背後にある割り当てのサイズを取得するには、libc mallocから直接返されます —カスタムヒープからではなく、オブジェクトの中央を指していません。 OS固有のAPI。便宜上、「ポータブルな」ラッパー関数にまとめました。このコードが機能しない一般的なシステムを見つけた場合は、コメントを残してください。修正を試みます。

#if defined(__linux__)
// https://linux.die.net/man/3/malloc_usable_size
#include <malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return malloc_usable_size((void*)p);
}
#Elif defined(__Apple__)
// https://www.unix.com/man-page/osx/3/malloc_size/
#include <malloc/malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return malloc_size(p);
}
#Elif defined(_WIN32)
// https://docs.Microsoft.com/en-us/cpp/c-runtime-library/reference/msize
#include <malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return _msize((void *)p);
}
#else
#error "oops, I don't know this system"
#endif

#include <stdio.h>
#include <stdlib.h>  // for malloc itself

int main() {
    void *p = malloc(42);
    size_t true_length = portable_ish_malloced_size(p);
    printf("%zu\n", true_length);
}

テスト済み:

1
Quuxplusone

これはうまくいくかもしれません、あなたのコードの小さな更新:

void* inc = (void*) (++p)
size=p-inc;

しかし、これは1になります。つまり、char*の場合、pに関連付けられたメモリになります。 int*の場合、結果は4になります。

合計割り当てを見つける方法はありません。

1
sarin

Quuxplusoneは次のように書きました。「ポインタの値からヒープのどの部分から来たのかを何らかの形で知ることができない限り、これらのvoid *で機能する関数を書くことは不可能です。」 Cで動的に割り当てられたメモリのサイズを決定する "

実際にWindowsでは、_msizeはポインタの値から割り当てられたメモリサイズを提供します。アドレスに割り当てられたメモリがない場合、エラーがスローされます。

int main()
{
    char* ptr1 = NULL, * ptr2 = NULL;
    size_t bsz;    
    ptr1 = (char*)malloc(10);
    ptr2 = ptr1;
    bsz = _msize(ptr2);
    ptr1++;
    //bsz = _msize(ptr1);   /* error */
    free(ptr2);

    return 0;
}

#defineコレクションをありがとう。これがマクロバージョンです。

#define MALLOC(bsz) malloc(bsz)
#define FREE(ptr) do { free(ptr); ptr = NULL; } while(0)
#ifdef __linux__
#include <malloc.h>
#define MSIZE(ptr) malloc_usable_size((void*)ptr)
#Elif defined __Apple__
#include <malloc/malloc.h>
#define MSIZE(ptr) malloc_size(const void *ptr)
#Elif defined _WIN32
#include <malloc.h>
#define MSIZE(ptr) _msize(ptr)
#else
#error "unknown system"
#endif
0
Thomas_M