如何从C中的指针获取数组的大小?

时间:2008-10-24 07:01:43

标签: c memory-management pointers malloc

我已经分配了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,但如果可能,我宁愿不知道。有什么想法吗?

14 个答案:

答案 0 :(得分:52)

不,如果不强烈依赖malloc的实施细节,就无法获得此信息。特别是,malloc可以分配比您请求更多的字节(例如,为了特定存储器架构中的效率)。重新设计代码会更好,以便您明确跟踪n。另一种方法是至少重新设计和更危险的方法(鉴于它是非标准的,滥用指针的语义,对于那些追随你的人来说将是一个维护噩梦):store malloc地址的长度为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);

编辑:只是为了扮演魔鬼的拥护者......我知道这些“解决方案”都需要重新设计,但让我们来解决它。 当然,上面介绍的解决方案只是一个(包装好的)结构的hacky实现。你也可以定义:

typedef struct { 
  unsigned int n;
  void *arr;
} arrInfo;

传递arrInfo而不是原始指针。

现在我们正在做饭。但只要你重新设计,为什么要停在这里?你真正想要的是一个抽象数据类型(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 ++。上帝帮助我们所有人。

答案 1 :(得分:16)

自己跟踪数组大小; free使用malloc链来释放分配的,它不一定与你请求的数组大小相同

答案 2 :(得分: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语言中,你总是可以编写自己的分配器,但是,当你只需要记住分配了多少内存时,可能会遇到太麻烦。

答案 3 :(得分:8)

有些编译器提供了msize()或类似函数(_msize()等),让你可以做到这一点

答案 4 :(得分:4)

我可以推荐一种可怕的方法吗?

按如下方式分配所有数组:

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

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

然后,您始终可以将数组转换为int *并访问第-1个元素。

确保free指针,而不是数组指针本身!

此外,这可能会导致可怕的错误,让你撕裂你的头发。也许你可以在API调用中包装alloc函数。

答案 5 :(得分:2)

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

答案 6 :(得分:2)

对于指针数组,您可以使用以NULL结尾的数组。然后可以像使用字符串一样确定长度。在您的示例中,您可以使用结构属性来标记然后结束。当然,这取决于是否存在不能为NULL的成员。因此,假设您有一个属性名称,需要为数组中的每个结构设置,然后您可以通过以下方式查询大小:


int size;
struct mystruct *cur;

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

size = cur - myarray;

顺便说一句,你的例子应该是calloc(n,sizeof(struct mystruct))。

答案 7 :(得分:2)

其他人讨论了普通c指针的限制以及stdlib.h的{​​{1}}实现。某些实现提供了返回分配的块大小的扩展,该大小可能大于请求的大小。

如果必须具有此行为,则可以使用或编写专门的内存分配器。最简单的方法是在malloc()函数周围实现一个包装器。有点像:

stdlib.h

答案 8 :(得分:2)

确实你的问题是 - “我能找出malloc'd(或calloc'd)数据块的大小”。正如其他人所说:不,不是以标准的方式。

但是有一些自定义malloc实现可以实现它 - 例如http://dmalloc.com/

答案 9 :(得分:1)

我不知道一种方法,但我想它会解决在malloc内部的问题,这通常是一个非常非常糟糕的主意。

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

编辑:如果你知道你应该重做代码,那么你就知道了,好吧,做吧。是的,尝试对malloc进行轮询可能会快速而容易,但确定n肯定会减少混淆并加强设计。

答案 10 :(得分:1)

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

答案 11 :(得分:1)

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

魔法在于对myMax的调用:

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

那太神奇了,不是吗?

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

myMax还希望数组中的元素数量为int。我通过使用sizeof()来获得该值,以给出数组的字节大小和数组的第一个元素,然后将总字节数除以每个元素中的字节数。 (我们不应该猜测或硬编码int的大小,因为它在某些系统上是2个字节,在某些系统上有4个像我的OS X Mac,在其他系统上可能是其他东西)。

注意:当您的数据可能包含不同数量的样本时,所有这一切都很重要。

这是测试代码:

#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;
}

答案 12 :(得分:0)

uClibc中,malloc.h中有一个MALLOC_SIZE宏:

/* 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))

答案 13 :(得分: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