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

时间:2010-04-16 05:49:03

标签: c memory free malloc

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

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)知识?

9 个答案:

答案 0 :(得分:30)

除了Klatchko关于标准没有提供的正确观点之外,真正的malloc / free实现通常会分配更多空间。例如。如果你要求12个字节,它可以提供16个字节(见A Memory Allocator,其中注意16是一个常见的大小)。所以它不需要知道你要求12个字节,只是它给你一个16字节的块。

答案 1 :(得分:18)

你无法得到它,因为C委员会并没有要求在标准中。

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

*((size_t *)ptr - 1)

或者也许:

*((size_t *)ptr - 2)

但这是否有效将取决于您使用的malloc的实现存储该数据的确切位置。

答案 2 :(得分:8)

虽然可以获取内存分配器在分配的块之前放置的元数据,但只有如果指针真正是指向动态分配的块的指针,它才会起作用。这会严重影响函数的效用,要求所有传递的参数都是指向这些块的指针,而不是简单的自动或静态数组。

重点是检查指针没有可移植的方式来知道它指向的内存类型。因此,虽然这是一个有趣的想法,但它并不是一个特别安全的主张。

安全且便携的方法是保留分配的第一个字以保持长度。 GCC(可能还有一些其他编译器)支持一种使用零长度数组结构实现此方法的非可移植方法,与便携式解决方案相比,这有点简化了代码:

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 ;

答案 3 :(得分:8)

在阅读Klatchko's answer之后,我自己尝试了它,而ptr[-1]确实存储了实际内存(通常超过我们要求的内存以防止分段错误)。

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

尝试使用不同的大小,GCC按如下方式分配内存:

  

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

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

答案 4 :(得分:4)

我知道这个帖子有点旧,但我还有话要说。有一个函数(或宏,我还没有检查过库)malloc_usable_size() - 获取从堆分配的内存块的大小。手册页指出它仅用于调试,因为它不会输出您要求的数字,而是输出已分配的数字,这个数字要大一些。注意它是一个GNU扩展。

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

答案 5 :(得分:3)

非标准方法是使用_msize()。使用此功能将使您的代码不可移植。此外,文档还不是很清楚它是否会返回传递给malloc()的数字或实际块大小(可能更大)。

答案 6 :(得分:2)

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

答案 7 :(得分:1)

您可以为商店大小分配更多内存:

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

答案 8 :(得分:0)

要回答有关delete []的问题,早期版本的C ++实际上要求您调用delete [n]并告诉运行时大小,因此不必存储它。可悲的是,这种行为被删除为“太混乱”。

(详见D&amp; E)。