Почему malloc выделяет количество байтов, отличное от запрошенного?
Вопрос
У меня есть этот кусок кода
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
int main(){
void *a, *b;
a = malloc(16);
b = malloc(16);
printf("\n block size (for a): %p-%p : %li", b, a, b-a);
a = malloc(1024);
b = malloc(1024);
printf("\n block size (for a): %p-%p : %li", b, a, b-a);
}
Разве это не должно печатать последний выделенный размер блока (16 или 1024)? Вместо этого он печатает 24 и 1032, поэтому объем выделенной памяти, кажется, имеет 8 дополнительных байтов.
Моя проблема (до создания этого контрольного примера) заключается в том, что я выполняю malloc()
функцию (1024 байта) и возвращаю выделенный результат. При проверке размера блока при возврате функции я получаю 516 блоков ... и я не понимаю, почему. Я предполагаю, что это может быть причиной повреждения памяти, которое происходит после некоторой обработки выделенных буферов:)
Изменить. Я видел как я могу получить размер массива из указателя в C? и, кажется, спрашивает то же самое, извините за перепост.
Я переделал мой пример в мой более конкретный код:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
short int * mallocStuff(long int number, short int base){
short int *array;
int size=1024;
array=(short int*)calloc(1,size);
//array=(short int*)malloc(size);
return array;
}
int main(){
short int **translatedArray;
translatedArray=malloc(4*sizeof(short int));
int i;
for(i=0;i<4;i++){
translatedArray[i]=mallocStuff(0,0);
if(i>0)
printf("\n block size (for a): %p-%p : %i",
translatedArray[i], translatedArray[i-1], translatedArray[i]-translatedArray[i-1]);
}
return 0;
}
И вывод
block size (for a): 0x804a420-0x804a018 : 516
block size (for a): 0x804a828-0x804a420 : 516
block size (for a): 0x804ac30-0x804a828 : 516
Согласно приведенному выше посту, который больше 1024. Я не прав?
Решение
У вас есть ошибка. Вместо:
translatedArray=malloc(4*sizeof(short int));
Вы должны иметь
translatedArray=malloc(4*sizeof(short int*));
Обратите внимание на отсутствующий указатель в вашем коде. Я подозреваю, что именно отсюда вытекает ваше наблюдаемое поведение.
<Ч> Также обратите внимание, что 0x804a420 - 0x804a018 = 1032
, а не 516
. Формула translatedArray[i] - translatedArray[i - 1]
дает вам количество элементов (коротких или коротких) между двумя адресами, а не количество байтов .
Другие советы
Во-первых, Malloc не дает никаких гарантий, что два последовательных вызова malloc возвращают последовательные указатели. Р>
Во-вторых, в зависимости от вашей конкретной архитектуры применяются разные правила выравнивания; иногда вы можете запросить один байт, но архитектура предпочитает распределения с интервалами в 8 или 4 байта.
В-третьих, для malloc требуются дополнительные данные, чтобы сохранить размер выделенного блока и т. д.
Не делайте предположений о том, что делает malloc, помимо того, что написано в документации!
Функция malloc
всегда выделяет немного больше, чем вы просите, чтобы хранить некоторую бухгалтерскую информацию. В конце концов, когда вы звоните free()
, ему нужно знать, насколько велик блок.
Кроме того, обычно short int
реализации округляют запрошенный размер до следующего кратного 8 или 16 или некоторому другому числу округления.
Обновление . Реальный ответ на ваш вопрос заключается в использовании вами типа <=>. При выполнении арифметики указателя (вычитания) между типизированными указателями C и C ++ возвращают разницу в числе вещей, на которые указывают. Поскольку вы указываете на <=>, размер которого составляет два байта, возвращаемое значение составляет половину того, что вы ожидаете.
С другой стороны, <=> всегда выделяет определенное количество байтов , независимо от того, к чему вы приведете результат впоследствии. Попробуйте это:
array=(short int*)malloc(sizeof(short int) * size);
Нет никаких гарантий, что два вызова malloc возвращают блоки, точно упакованные вместе - фактически нет никаких гарантий относительно результата, за исключением того, что, если он не равен NULL, он будет указывать на блок, по меньшей мере такой же большой, как и блок просил.
Внутренне, большинство malloc хранят рабочие данные, чтобы помочь им управлять кучей. Например, эти 8 байтов могут содержать два указателя - один указывает на следующий блок, а другой - на предыдущий блок. Я не знаю, что это за 8 байтов, потому что вы не упомянули, на какой ОС вы работаете, но для malloc вполне нормально использовать некоторую память за кулисами.
Некоторые распределители (например, в окнах) предоставляют библиотечную функцию для определения размера блока по указателю, однако некоторые этого не делают, поскольку это довольно эзотерическая особенность.
То, что возвращает malloc, зависит от реализации malloc и архитектуры. Как уже говорили другие, вы гарантированно получите по крайней мере запрошенный объем памяти, или NULL. По этой же причине иногда вы можете писать после конца массива и не получать ошибку сегментации. Это потому, что у вас действительно есть действительный доступ к этой памяти, вы просто не знали об этом.
malloc () обычно реализуется путем разбиения доступной кучи на куски разных размеров. В вашем случае malloc () возвращает 2 последовательных 1024 (или 16) байтовых блока. Упоминаемое вами 8-байтовое пространство используется malloc () для бухгалтерской информации.
См. примечания Doug Lea malloc (), чтобы понять, что происходит за кулисами:
Не говоря уже о том, что нет никаких гарантий, что 2 последовательных распределения будут рядом друг с другом. malloc()
будет иметь свои собственные накладные расходы. Р>
Если malloc
возвращает что-либо, кроме нуля, то объем памяти, который был выделен для вашей программы , имеет размер, который вы передали <=>. Принятие разности указателей между возвращаемыми значениями двух разностных вызовов для <=> может иметь любое значение и не имеет ничего общего с размером блока первого выделенного блока.
Я нашел это ... и проверьте ссылку ниже для получения дополнительной информации.
Выделение
Блок выделяется из свободного пула путем предварительного преобразования запрошенных байтов в индекс в массиве сегментов, используя следующее уравнение:
необходимо = запрошено + 8
При необходимости < = 16, затем ведро = 0
При необходимости > 16, затем ведро = (log (необходимо) / log (2) округлено до ближайшего целого числа) - 3
Размер каждого блока в списке, привязанном к сегменту, равен размеру блока = 2 блока + 4. Если список в сегменте равен нулю, память выделяется с помощью функции sbrk для добавления блоков в список. Если размер блока меньше, чем страница, то страница выделяется с помощью подпрограммы sbrk, и число блоков, полученных путем деления размера блока на размер страницы, добавляется в список. Если размер блока равен или больше, чем страница, необходимая память выделяется с помощью подпрограммы sbrk, и в свободный список для блока добавляется один блок. Если свободный список не пуст, блок в начале списка возвращается вызывающей стороне. Следующий блок в списке становится новым заголовком.
Перед указателем стоит размер следующего массива, представляющего собой 32/64-битное целое число (не знаю, подписан он или нет)
таким образом, объем выделенной памяти, по-видимому, имеет 8 дополнительных байтов? malloc()
ваша система выделяет дополнительные байты для хранения метаданных, таких как размер раздела кучи, начальный адрес и т.д. Р>
Хотя это зависит от платформы.
В моих системах X86 17
выделяет минимум malloc(0)
байтов, даже если я запрашиваю <=>. Р>
int main(void) {
int *p = malloc(0);
if(p == NULL) {
/* error handling */
}
printf("%d\n",p[-1]);/ *it prints 17 bytes */
/* some code */
return 0;
}
malloc () может выделять непрерывную память, но когда вы вызываете malloc () 2 раза и не можете ожидать, что выделенная память будет смежной, вычитая переменные двух указателей ...
Однако выделенная память - это виртуальная память, которая является частью реализации ядра, а именно управление памятью (VFS). Это может не повлиять на функциональность приложения.