web-dev-qa-db-ja.com

Cのポインターから配列のサイズを取得するにはどうすればよいですか?

次のように、サイズmystructnの「配列」を割り当てました。

_if (NULL == (p = calloc(sizeof(struct mystruct) * n,1))) {
 /* handle error */
}
_

後で、pにのみアクセスでき、nはなくなりました。ポインタpのみを指定して配列の長さを決定する方法はありますか?

free(p)はまさにそれを行うので、mustが可能であると考えています。 malloc()が割り当てたメモリの量を追跡していることを知っているので、長さを知っています。おそらく、この情報を照会する方法はありますか?何かのようなもの...

_int length = askMallocLibraryHowMuchMemoryWasAlloced(p) / sizeof(mystruct)
_

nがわかるようにコードを修正し直す必要があることはわかっていますが、可能であればできません。何か案は?

66
Joel

いいえ、mallocの実装の詳細に強く依存せずにこの情報を取得する方法はありません。特に、mallocは要求よりも多くのバイトを割り当てる場合があります(たとえば、特定のメモリアーキテクチャの効率のため)。 nを明示的に追跡できるように、コードを再設計することをお勧めします。代替策は、少なくとも再設計とはるかに危険なアプローチです(非標準であると仮定すると、ポインタのセマンティクスを乱用し、あなたの後に来るもののメンテナンスの悪夢):mallocされたアドレスにlength nを格納し、その後に配列を格納します。割り当ては次のようになります。

_void *p = calloc(sizeof(struct mystruct) * n + sizeof(unsigned long int),1));
*((unsigned long int*)p) = n;
_

n*((unsigned long int*)p)に格納され、配列の開始は現在

_void *arr = p+sizeof(unsigned long int);
_

編集:悪魔の擁護者を演じるためだけに...これらの「解決策」はすべて再設計が必要であることを知っていますが、試してみましょう。もちろん、上記のソリューションは、(十分に詰め込まれた)構造体のハックな実装にすぎません。以下を定義することもできます。

_typedef struct { 
  unsigned int n;
  void *arr;
} arrInfo;
_

生のポインタではなく、arrInfosを渡します。

今、私たちは料理しています。しかし、あなたが再設計している限り、なぜここに止まるのですか?本当に必要なのは、抽象データ型(ADT)です。アルゴリズムとデータ構造クラスの紹介テキストはそれを行います。 ADTは、データ型のパブリックインターフェイスを定義しますが、そのデータ型の実装を隠します。したがって、公開されている配列のADTは次のようになります。

_typedef void* arrayInfo;
(arrayInfo)newArrayInfo(unsignd int n, unsigned int itemSize);
(void)deleteArrayInfo(arrayInfo);
(unsigned int)arrayLength(arrayInfo);
(void*)arrayPtr(arrayInfo);
...
_

言い換えると、ADTはデータと動作のカプセル化の形式です...つまり、ストレートCを使用したオブジェクト指向プログラミングに可能な限り近いものです。 C++コンパイラを使用している場合は、完全に独り占めしてSTL _std::vector_を使用することもできます。

そこで、Cについて簡単な質問をして、C++になりました。神は私たちすべてを助けます。

52
Barry Wark

自分で配列サイズを追跡します。 freeはmallocチェーンを使用して、割り当てられたblockを解放します。これは、要求した配列と同じサイズである必要はありません

16
Steven A. Lowe

前の答えを確認するために:ポインターを調べるだけでは、このポインターを返したmallocによって割り当てられたメモリー量を知る方法はありません。

うまくいったらどうしますか?

これが不可能な理由の一例。ポインターに割り当てられたメモリを返すget_size(void *)という仮想関数を使用したコードを想像してみましょう。

typedef struct MyStructTag
{ /* etc. */ } MyStruct ;

void doSomething(MyStruct * p)
{
   /* well... extract the memory allocated? */
   size_t i = get_size(p) ;
   initializeMyStructArray(p, i) ;
}

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   doSomething(s) ;
}

なぜ機能したとしても、とにかく機能しないのでしょうか?

しかし、このアプローチの問題は、Cではポインター演算を使用できることです。 doSomethingElse()を書き直しましょう:

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   MyStruct * s2 = s + 5 ; /* s2 points to the 5th item */
   doSomething(s2) ; /* Oops */
}

関数に有効なポインタを送信しましたが、mallocによって返されたポインタではないため、get_sizeの動作方法を想定しています。 get_sizeがサイズを見つけるためにすべての問題を(つまり、非効率的な方法で)行ったとしても、この場合、コンテキストで間違っている値を返します。

結論

この問題を回避する方法は常にあり、Cではいつでも独自のアロケーターを作成できますが、割り当てられたメモリーの量を覚えるだけで十分な場合は、おそらく面倒です。

9
paercebal

一部のコンパイラは、msize()または同様の関数(_msize()など)を提供します。

8
dmityugov

ひどい方法をお勧めしますか?

すべての配列を次のように割り当てます。

void *blockOfMem = malloc(sizeof(mystruct)*n + sizeof(int));

((int *)blockofMem)[0] = n;
mystruct *structs = (mystruct *)(((int *)blockOfMem) + 1);

その後、いつでも配列をint *にキャストし、-1番目の要素にアクセスできます。

配列ポインター自体ではなく、必ずそのポインターをfreeしてください!

また、これは恐ろしいバグを引き起こし、髪を引き裂く可能性があります。たぶん、API呼び出しなどでalloc funcsをラップできます。

4
Claudiu

mallocは、少なくとも要求された大きさ以上のメモリブロックを返します。そのため、ブロックサイズを照会できたとしても、配列サイズは確実に得られません。そのため、自分でコードを追跡するためにコードを修正する必要があります。

2
David Arno

本当にあなたの質問は-「malloc'd(またはcalloc'd)データブロックのサイズを調べることができます」です。そして、他の人が言ったように:いいえ、標準的な方法ではありません。

ただし、それを行うカスタムmalloc実装があります。たとえば、- http://dmalloc.com/

2
pm100

ポインターの配列には、NULLで終わる配列を使用できます。この長さは、文字列で行われるように決定できます。この例では、構造属性を使用して、マークしてから終了することができます。もちろん、NULLにできないメンバーがいるかどうかによって異なります。配列内のすべての構造体に設定する必要がある属性名があるとします。その後、次の方法でサイズを照会できます。


int size;
struct mystruct *cur;

for (cur = myarray; cur->name != NULL; cur++)
    ;

size = cur - myarray;

ところで、あなたの例ではcalloc(n、sizeof(struct mystruct))でなければなりません。

2
quinmars

他の人は、プレーンなcポインターの制限とmalloc()stdlib.h実装について議論しています。一部の実装では、要求されたサイズよりも大きい可能性のあるallocatedブロックサイズを返す拡張機能を提供しています。

mustにこの動作がある場合は、専用のメモリアロケーターを使用または作成できます。この最も簡単なことは、stdlib.h関数のラッパーを実装することです。何かのようなもの:

void* my_malloc(size_t s);     /* Calls malloc(s), and if successful stores 
                                  (p,s) in a list of handled blocks */
void my_free(void* p);         /* Removes list entry and calls free(p) */
size_t my_block_size(void* p); /* Looks up p, and returns the stored size */
...
2
dmckee

Mallocライブラリにブロックの大きさを尋ねることができない理由の1つは、アロケーターが通常、最小粒度要件(16バイトなど)を満たすためにリクエストのサイズを切り上げることです。したがって、5バイトを要求すると、サイズ16のブロックが返されます。 16を使用して5で割ると、実際には1つだけを割り当てたときに3つの要素が得られます。そもそもmallocライブラリが要求したバイト数を追跡​​するには余分なスペースが必要になるため、自分で追跡するのが最善です。

1
Greg Hewgill

私は方法を知りませんが、それは一般的に非常に非常に悪いアイデアであるmallocの内部でいじくり回すことに対処すると想像します。

割り当てたメモリのサイズを保存できないのはなぜですか?

編集:あなたがnを知っているようにコードをやり直す必要があることがわかっている場合は、まあ、それを行います。はい、mallocをポーリングするのは簡単で迅速かもしれませんが、nを確認することで混乱を最小限に抑え、設計を強化できます。

1
Bob Somers

これは私のソートルーチンのテストです。フロート値を保持するために7つの変数をセットアップし、それらを最大値を見つけるために使用される配列に割り当てます。

魔法はmyMaxの呼び出しにあります。

float mmax = myMax((float *)&arr、(int)sizeof(arr)/ sizeof(arr [0]));

そして、それは魔法でしたね。

myMaxはfloat配列ポインター(float *)を想定しているため、&arrを使用して配列のアドレスを取得し、floatポインターとしてキャストします。

myMaxは、配列内の要素数をintとしても期待しています。 sizeof()を使用して配列のサイズと配列の最初の要素を取得し、合計バイトを各要素のバイト数で除算することでその値を取得します。 (intのサイズを推測したり、ハードコーディングしたりしないでください。これは、一部のシステムでは2バイトであり、私のOS X Macのようなシステムでは4バイトであり、他のシステムでは別のサイズになる可能性があるためです)。

注:データのサンプル数が異なる場合、これはすべて重要です。

テストコードは次のとおりです。

#include <stdio.h>

float a, b, c, d, e, f, g;

float myMax(float *apa,int soa){
 int i;
 float max = apa[0];
 for(i=0; i< soa; i++){
  if (apa[i]>max){max=apa[i];}
  printf("on i=%d val is %0.2f max is %0.2f, soa=%d\n",i,apa[i],max,soa);
 }
 return max;
}

int main(void)
{
 a = 2.0;
 b = 1.0;
 c = 4.0;
 d = 3.0;
 e = 7.0;
 f = 9.0;
 g = 5.0;
 float arr[] = {a,b,c,d,e,f,g};

 float mmax = myMax((float *)&arr,(int) sizeof(arr)/sizeof(arr[0]));
 printf("mmax = %0.2f\n",mmax);

 return 0;
}
1
Wm J

Clibc には、MALLOC_SIZEマクロの malloc.h

/* The size of a malloc allocation is stored in a size_t Word
   MALLOC_HEADER_SIZE bytes prior to the start address of the allocation:

     +--------+---------+-------------------+
     | SIZE   |(unused) | allocation  ...   |
     +--------+---------+-------------------+
     ^ BASE             ^ ADDR
     ^ ADDR - MALLOC_HEADER_SIZE
*/

/* The amount of extra space used by the malloc header.  */
#define MALLOC_HEADER_SIZE          \
  (MALLOC_ALIGNMENT < sizeof (size_t)       \
   ? sizeof (size_t)                \
   : MALLOC_ALIGNMENT)

/* Set up the malloc header, and return the user address of a malloc block. */
#define MALLOC_SETUP(base, size)  \
  (MALLOC_SET_SIZE (base, size), (void *)((char *)base + MALLOC_HEADER_SIZE))
/* Set the size of a malloc allocation, given the base address.  */
#define MALLOC_SET_SIZE(base, size) (*(size_t *)(base) = (size))

/* Return base-address of a malloc allocation, given the user address.  */
#define MALLOC_BASE(addr)   ((void *)((char *)addr - MALLOC_HEADER_SIZE))
/* Return the size of a malloc allocation, given the user address. */
#define MALLOC_SIZE(addr)   (*(size_t *)MALLOC_BASE(addr))
0