Я знаю, что это - общая конвенция передать длину динамично выделенных массивов к функциям, которые управляют ими:
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) знание от?
Помимо правильного замечания Клачко о том, что стандарт не предусматривает этого, реальные реализации malloc/free часто выделяют больше места, чем вы просите. Например, если вы просите 12 байт, она может выделить 16 (см. A Memory Allocator, где отмечается, что 16 - обычный размер). Поэтому ему не нужно знать, что вы запросили 12 байт, достаточно знать, что он предоставил вам 16-байтовый кусок.
Вы не можете получить это, потому что комитет C не требует этого в стандарте.
Если вы хотите написать непереносимый код, вам может повезти:
*((size_t *)ptr - 1)
или, может быть,:
*((size_t *)ptr - 2)
Но будет ли это работать, будет зависеть от того, где именно реализация malloc, которую вы используете, хранит эти данные.
Нестандартный способ - использовать _msize ()
. Использование этой функции сделает ваш код непереносимым. Также в документации не очень ясно, вернет ли он число, переданное в malloc ()
, или реальный размер блока (может быть больше).
Как хранить эти данные - решать разработчику malloc
. Чаще всего длина хранится непосредственно перед выделенной памятью (то есть, если вы хотите выделить 7 байтов, на самом деле выделяется 7 + x байтов, где x дополнительных байтов используются для хранения метаданных). Иногда метаданные сохраняются до и после выделенной памяти для проверки на наличие повреждений кучи. Но разработчик также может использовать дополнительную структуру данных для хранения метаданных.
Прочитав ответ Клатчко , я сам попробовал, и ptr [-1]
действительно хранит фактическую память (обычно больше, чем память, которую мы просили возможно, чтобы уберечься от ошибки сегментации).
{
char *a = malloc(1);
printf("%u\n", ((size_t *)a)[-1]); //prints 17
free(a);
exit(0);
}
Пробуя разные размеры, GCC распределяет память следующим образом:
Первоначально выделенная память составляет 17 байт.
Выделенная память как минимум на 5 байтов больше запрошенного размера. Если запрашивается больше, выделяется на 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 ;
Вы можете выделить больше памяти для хранения размера:
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;
}