我怎样才能从C中的指针获取数组的大小?
我已经分配了一个大小为n
的mystruct
的“数组”:
if (NULL == (p = calloc(sizeof(struct mystruct) * n,1))) { /* handle error */ }
之后,我只能访问p
,不再有n
。 有没有一种方法来确定只给了指针p
数组的长度?
我认为这一定是可能的,因为free(p)
就是这样做的。 我知道malloc()
跟踪它已经分配了多less内存,这就是为什么它知道长度; 也许有一种方法来查询这些信息? 就像是…
int length = askMallocLibraryHowMuchMemoryWasAlloced(p) / sizeof(mystruct)
我知道我应该修改代码,以便知道n
,但是如果可能,我宁愿不要。 有任何想法吗?
不,如果不依赖于malloc
的实现细节,就无法得到这些信息。 特别是, malloc
可能会分配比您请求的更多的字节(例如,为了在特定内存体系结构中的效率)。 重新devise你的代码会更好,这样你可以明确地跟踪n
。 另一种方法至less是重新devise和更危险的方法(因为它是非标准的,滥用指针的语义,并将成为后面的维护者的噩梦):将长度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);
编辑:只是扮演魔鬼的拥护者…我知道这些“解决scheme”都需要重新devise,但是让我们来玩吧。 当然,上面介绍的解决scheme只是一个(精心打包)结构的hacky实现。 你可以定义:
typedef struct { unsigned int n; void *arr; } arrInfo;
并传递arrInfo
而不是原始的指针。
现在我们正在做饭。 但只要你在重新devise,为什么要在这里停下来呢? 你真正想要的是一个抽象的数据types(ADT)。 任何algorithm和数据结构类的介绍性文本都可以做到这一点。 ADT定义了数据types的公共接口,但隐藏了该数据types的实现。 因此,公开ADT的数组可能看起来像
typedef void* arrayInfo; (arrayInfo)newArrayInfo(unsignd int n, unsigned int itemSize); (void)deleteArrayInfo(arrayInfo); (unsigned int)arrayLength(arrayInfo); (void*)arrayPtr(arrayInfo); ...
换句话说,ADT是一种数据和行为封装的forms…换句话说,就像你可以用直接的C来进行面向对象的编程一样。除非你被困在一个平台上有一个C ++编译器,你也可以去整个猪,只是使用STL std::vector
。
在那里,我们已经谈到了一个关于C的简单问题,并以C ++结束。 上帝帮助我们。
跟踪自己的数组大小; free使用malloc链来释放已分配的块 ,该块不一定与您请求的数组大小相同
只是为了确认以前的答案:没有办法知道,只是通过学习一个指针,一个malloc分配了多less内存,它返回了这个指针。
如果它有效?
为什么这是不可能的一个例子。 让我们设想一下带有假设函数的代码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经历了所有的麻烦以find大小(即以一种低效的方式),在这种情况下,它将返回一个在你的上下文中是错误的值。
结论
总是有办法避免这个问题,而在C中,你可以随时编写自己的分配器,但是当你需要记住分配了多less内存的时候,这也许是太麻烦了。
一些编译器提供msize()或类似的函数(_msize()等),可以让你做到这一点
我可以推荐一个可怕的方法来做到这一点吗?
分配所有的数组,如下所示:
void *blockOfMem = malloc(sizeof(mystruct)*n + sizeof(int)); ((int *)blockofMem)[0] = n; mystruct *structs = (mystruct *)(((int *)blockOfMem) + 1);
那么你总是可以把你的数组转换为int *
并访问-1st元素。
一定要free
该指针,而不是数组指针本身!
此外,这可能会导致可怕的错误,将让你撕下你的头发。 也许你可以把分配函数包装在API调用中。
malloc将返回一块至less与你所要求的一样大的内存块,但可能更大。 所以,即使你可以查询块的大小,这不会可靠地给你你的数组大小。 所以你只需要修改你的代码来自己跟踪它。
对于指针数组,您可以使用以NULL结尾的数组。 长度然后可以像使用string那样确定。 在你的例子中,你可以使用一个结构属性来标记结束。 当然,这取决于是否有一个不能为NULL的成员。 所以可以说你有一个属性名称,需要为你的数组中的每个结构体设置,然后你可以通过以下方式查询大小:
int size; struct mystruct *cur; for (cur = myarray; cur->name != NULL; cur++) ; size = cur - myarray;
顺便说一句,它应该是calloc(n,sizeof(struct mystruct))在你的例子。
其他人讨论了纯C指针的限制和malloc()
的stdlib.h
实现。 一些实现提供扩展,它返回分配的块大小,可能大于请求的大小。
如果你必须有这种行为,你可以使用或写一个专门的内存分配器。 这个最简单的事情就是围绕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 */ ...
真的你的问题是 – “我可以找出一个malloc'd(或calloc'd)数据块的大小”。 正如其他人所说:不,不是以一种标准的方式。
但是,有自定义的malloc实现,例如http://dmalloc.com/
我不知道有什么办法,但我会想象它会在malloc的内部进行处理,这通常是一个非常非常糟糕的主意。
为什么你不能存储你分配的内存大小?
编辑:如果你知道你应该重写代码,所以你知道,呃,做到这一点。 是的,可以快速而简单地尝试查询malloc,但是肯定知道n会最小化混淆并加强devise。
你不能要求malloc库有多大块的原因之一是分配器通常会将你的请求的大小加起来,以满足一些最小的粒度要求(例如16字节)。 所以,如果你要求5个字节,你会得到一个大小为16的块。 如果你把16和5分开,当你真的只分配一个时,你会得到三个元素。 malloc库需要额外的空间来跟踪你首先要求的字节数,所以最好自己跟踪一下。
这是我的sorting程序的testing。 它设置了7个variables来保存浮点值,然后将它们分配给一个用于查找最大值的数组。
神奇的是在对myMax的调用中:
float mmax = myMax((float *)&arr,(int)sizeof(arr)/ sizeof(arr [0]));
那真是神奇,不是吗?
myMax需要一个float数组指针(float *),所以我使用&arr来获取数组的地址,并将其转换为浮点型指针。
myMax也希望数组中元素的个数为int。 我通过使用sizeof()来获取该值,以给出数组的字节大小和数组的第一个元素,然后将总字节数除以每个元素中的字节数。 (我们不应该猜测或硬编码一个int的大小,因为它在某些系统上是2个字节,有些像我的OS X Mac上是4个字节,而另外一些则可能是其他的)。
注意:当您的数据可能有不同数量的样本时,所有这一切都很重要。
以下是testing代码:
#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; }
在uClibc中 , MALLOC_SIZE
中有一个MALLOC_SIZE
macros:
/* 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))