如果free()知道我的数组的长度,为什么我不能在我自己的代码中请求它?

我知道将dynamic分配数组的长度传递给操作它们的函数是一个常见的约定:

void initializeAndFree(int* anArray, size_t length); int main(){ size_t arrayLength = 0; scanf("%d", &arrayLength); int* myArray = (int*)malloc(sizeof(int)*arrayLength); initializeAndFree(myArray, arrayLength); } void initializeAndFree(int* anArray, size_t length){ int i = 0; for (i = 0; i < length; i++) { anArray[i] = 0; } free(anArray); } 

但是如果没有办法让我从指针中获取分配的内存的长度,那么free()自动“知道当我给它的东西是什么时候释放的是同一个指针? 为什么我不能像C程序员那样获得魔法?

free()从哪里获得免费(har-har)知识?

除了Klatchko的标准没有规定的正确的观点之外,真正的malloc / free实现往往会分配更多的空间,然后你要求。 例如,如果你要求12个字节,它可能会提供16个(请参阅内存分配器 ,它指出16是一个常见的大小)。 所以它不需要知道你要求12字节,只是它给了你一个16字节的块。

你不能得到它,因为C委员会不要求这个标准。

如果你愿意写一些不可移植的代码,你可能有运气:

 *((size_t *)ptr - 1) 

或者可能:

 *((size_t *)ptr - 2) 

但是这个工作是否将取决于你正在使用的malloc的实现在哪里存储数据。

虽然有可能获得内存分配器放置在分配块之前的元数据,但只有指针确实是指向dynamic分配块的指针时才能使用。 这将严重影响函数的效用,要求所有传递的参数都是指向这些块的指针,而不是说简单的自动或静态数组。

关键是从指针的检查中不能移植到知道指向哪种types的内存。 所以虽然这是一个有趣的想法,但这不是一个特别安全的提议。

一个安全和便携的方法是保留分配的第一个字来保存长度。 GCC(也许还有其他一些编译器)支持一种不可移植的方法来实现这一点,它使用一个长度为零的结构,与便携式解决scheme相比,它简化了代码:

 typedef tSizedAlloc { size_t length ; char* alloc[0] ; // Compiler specific extension!!! } ; // Allocating a sized block tSizedAlloc* blk = malloc( sizeof(tSizedAlloc) + length ) ; blk->length = length ; // Accessing the size and data information of the block size_t blk_length = blk->length ; char* data = blk->alloc ; 

在阅读Klatchko的回答之后 ,我自己试了一下, ptr[-1]确实存储了实际的内存(通常比我们要求的内存大得多,以避免分段错误)。

 { char *a = malloc(1); printf("%u\n", ((size_t *)a)[-1]); //prints 17 free(a); exit(0); } 

尝试不同的大小,GCC分配内存如下:

最初分配的内存是17个字节。
分配的内存至less比请求的大小多5个字节,如果有更多的请求,则分配8个字节。

  • 如果size是[0,12],则分配的内存是17。
  • 如果大小是[13],分配的内存是25。
  • 如果大小是[20],分配的内存是25。
  • 如果size是[21],分配的内存是33。

我知道这个线程有点老,但我还是有话要说。 有一个函数(或一个macros,我还没有检查库)malloc_usable_size() – 获取从堆分配的内存块的大小。 手册页指出它只是用于debugging,因为它输出的不是你询问的数字,而是它分配的数字,这个数字有点大。 注意这是一个GNU扩展。

另一方面,它可能甚至不需要,因为我相信释放内存块你不必知道它的大小。 只要删除负责块的句柄/描述符/结构即可。

一个非标准的方法是使用_msize() 。 使用这个函数将使你的代码变得不可移植。 另外文档不是很清楚它将返回传递给malloc()还是实际的块大小(可能会更大)。

这取决于malloc实现者如何存储这些数据。 大多数情况下,长度直接存储在分配的内存之前(也就是说,如果要分配7个字节,实际上将分配7 + x个字节,其中x个附加字节用于存储元数据)。 有时,元数据既存储在分配的内存之前,也存储在检查堆损坏之后。 但是实现者也可以select使用额外的数据结构来存储元数据。

你可以分配更多的内存来存储大小:

 void my_malloc(size_t n,size_t size ) { void *p = malloc( (n * size) + sizeof(size_t) ); if( p == NULL ) return NULL; *( (size_t*)p) = n; return (char*)p + sizeof(size_t); } void my_free(void *p) { free( (char*)p - sizeof(size_t) ); } void my_realloc(void *oldp,size_t new_size) { ... } int main(void) { char *p = my_malloc( 20, 1 ); printf("%lu\n",(long int) ((size_t*)p)[-1] ); return 0; } 

为了回答关于delete []的问题,C ++的早期版本实际上需要你调用delete [n]并告诉运行时的大小,所以它不需要存储它。 可悲的是,这种行为被解除为“太混乱”。

(详情请参阅D&E。)