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'd地址,后跟数组。那么分配将是:

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);

编辑:只是为了扮演魔鬼的拥护者......我知道这些“解决方案”都需要重新设计,但让我们发挥一下。当然,上面提出的解决方案只是一个(包装良好的)结构的 hacky 实现。您不妨定义:

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++。上帝帮助我们所有人。

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

get_size 应该如何工作,因为您向函数发送了一个有效指针,但不是 malloc 返回的指针。即使 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该指针,而不是数组指针本身!

此外,这可能会导致可怕的错误,让你把头发扯掉。也许您可以将 alloc 函数包装在 API 调用或其他东西中。

于 2008-10-24T07:28:50.973 回答
2

malloc 将返回至少与您请求一样大的内存块,但可能更大。因此,即使您可以查询块大小,这也不能可靠地为您提供数组大小。所以你只需要修改你的代码来自己跟踪它。

于 2008-10-24T07:10:29.823 回答
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.hmalloc(). 一些实现提供了扩展,这些扩展返回可能大于请求大小的分配块大小。

如果你必须有这种行为,你可以使用或编写一个专门的内存分配器。最简单的做法是在函数周围实现一个包装器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'd(或 calloc'd)数据块的大小”。正如其他人所说:不,不是以标准方式。

但是有自定义 malloc 实现可以做到这一点 - 例如http://dmalloc.com/

于 2010-09-03T16:18:58.550 回答
1

我不知道有什么方法,但我想它会处理 malloc 内部的混乱,这通常是一个非常非常糟糕的主意。

为什么不能存储分配的内存大小?

编辑:如果你知道你应该重新编写代码,这样你就知道了,那就去做吧。是的,尝试轮询 malloc 可能既快速又容易,但知道 n 肯定会最大程度地减少混乱并加强设计。

于 2008-10-24T07:04:07.373 回答
1

您不能询问 malloc 库一个块有多大的原因之一是分配器通常会四舍五入您的请求的大小以满足一些最小粒度要求(例如,16 字节)。因此,如果您要求 5 个字节,您将得到一个大小为 16 的块。如果你取 16 除以 5,当你真的只分配一个元素时,你会得到三个元素。malloc 库首先需要额外的空间来跟踪您请求的字节数,因此最好自己跟踪。

于 2008-10-24T07:11:51.227 回答
1

这是对我的排序程序的测试。它设置了 7 个变量来保存浮点值,然后将它们分配给一个数组,该数组用于查找最大值。

神奇之处在于对 myMax 的调用:

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

这很神奇,不是吗?

myMax 需要一个浮点数组指针 (float *),所以我使用 &arr 来获取数组的地址,并将其转换为浮点指针。

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;
}
于 2010-09-03T15:58:54.660 回答
0

uClibc中,有一个MALLOC_SIZEmalloc.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 回答
0

malloc()在实际分配的空间的 8 个字节之前存储有关空间分配的元数据。这可以用来确定缓冲区的空间。在我的 x86-64 上,这总是返回 16 的倍数。因此,如果分配的空间是 16 的倍数(在大多数情况下),那么可以使用:

代码

#include <stdio.h>
#include <malloc.h>

int size_of_buff(void *buff) {
        return ( *( ( int * ) buff - 2 ) - 17 ); // 32 bit system: ( *( ( int * ) buff - 1 ) - 17 )
}

void main() {
        char *buff = malloc(1024);
        printf("Size of Buffer: %d\n", size_of_buff(buff));
}

输出

Size of Buffer: 1024
于 2021-07-20T10:35:55.537 回答