69

次のようなmystructサイズの「配列」を割り当てました。n

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

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

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

int length = askMallocLibraryHowMuchMemoryWasAlloced(p) / sizeof(mystruct)

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

4

14 に答える 14

54

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

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;

arrInfo生のポインターではなく sを渡します。

今、私たちは料理をしています。しかし、再設計している限り、なぜここでやめるのでしょうか? 本当に必要なのは、抽象データ型 (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++ にたどり着きました。神は私たち全員を助けてくださいます.

于 2008-10-24T07:16:19.330 に答える
16

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

于 2008-10-24T07:03:41.523 に答える
9

以前の回答を確認するだけです。ポインターを調べるだけでは、このポインターを返した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 ではいつでも独自のアロケータを記述できますが、必要なのは、割り当てられたメモリの量を覚えておくことだけである場合、おそらく面倒です。

于 2008-10-24T07:23:02.683 に答える
8

一部のコンパイラは msize() または同様の関数 (_msize() など) を提供しており、まさにそれを行うことができます。

于 2008-10-24T09:16:48.150 に答える
4

それを行うためのひどい方法をお勧めできますか?

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

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

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

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

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

また、これは恐ろしいバグを引き起こし、髪を引き裂く可能性があります. おそらく、API呼び出しなどで割り当て関数をラップできます。

于 2008-10-24T07:28:50.973 に答える
2

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


int size;
struct mystruct *cur;

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

size = cur - myarray;

ところで、あなたの例ではcalloc(n、sizeof(struct mystruct))である必要があります。

于 2008-10-24T08:59:48.370 に答える
2

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

この動作が必要な場合は、専用のメモリアロケータを使用または作成できます。この最も簡単な方法は、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 */
...
于 2008-10-24T17:29:43.430 に答える
2

malloc は、少なくとも要求したサイズと同じ大きさのメモリ ブロックを返しますが、それよりも大きい可能性もあります。したがって、ブロック サイズを照会できたとしても、配列サイズが確実に得られるわけではありません。そのため、コードを変更して自分で追跡する必要があります。

于 2008-10-24T07:10:29.823 に答える
2

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

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

于 2010-09-03T16:18:58.550 に答える
1

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

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

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

それは魔法のようでしたね。

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

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

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

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

#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;
}
于 2010-09-03T15:58:54.660 に答える
1

私は方法を認識していませんが、mallocの内部をいじることに対処することになると思いますが、これは一般的に非常に悪い考えです。

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

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

于 2008-10-24T07:04:07.373 に答える
1

ブロックの大きさを malloc ライブラリに問い合わせることができない理由の 1 つは、アロケータが通常、最小粒度要件 (たとえば、16 バイト) を満たすために要求のサイズを切り上げることです。したがって、5 バイトを要求すると、サイズ 16 のブロックが返されます。16 を 5 で割ると、実際には 1 つしか割り当てていないのに 3 つの要素が得られます。最初に要求したバイト数を malloc ライブラリが追跡するには余分なスペースが必要になるため、自分で追跡することをお勧めします。

于 2008-10-24T07:11:51.227 に答える
0

uClibcには、次の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))
于 2015-09-18T23:37:45.913 に答える