Почему malloc выделяет количество байтов, отличное от запрошенного?

StackOverflow https://stackoverflow.com/questions/430163

  •  08-07-2019
  •  | 
  •  

Вопрос

У меня есть этот кусок кода

#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 (), чтобы понять, что происходит за кулисами:

malloc() будет иметь свои собственные накладные расходы.

Не говоря уже о том, что нет никаких гарантий, что 2 последовательных распределения будут рядом друг с другом.

Если malloc возвращает что-либо, кроме нуля, то объем памяти, который был выделен для вашей программы , имеет размер, который вы передали <=>. Принятие разности указателей между возвращаемыми значениями двух разностных вызовов для <=> может иметь любое значение и не имеет ничего общего с размером блока первого выделенного блока.

Я нашел это ... и проверьте ссылку ниже для получения дополнительной информации.

Выделение

Блок выделяется из свободного пула путем предварительного преобразования запрошенных байтов в индекс в массиве сегментов, используя следующее уравнение:

необходимо = запрошено + 8

При необходимости < = 16, затем ведро = 0

При необходимости > 16, затем ведро = (log (необходимо) / log (2) округлено до ближайшего целого числа) - 3

Размер каждого блока в списке, привязанном к сегменту, равен размеру блока = 2 блока + 4. Если список в сегменте равен нулю, память выделяется с помощью функции sbrk для добавления блоков в список. Если размер блока меньше, чем страница, то страница выделяется с помощью подпрограммы sbrk, и число блоков, полученных путем деления размера блока на размер страницы, добавляется в список. Если размер блока равен или больше, чем страница, необходимая память выделяется с помощью подпрограммы sbrk, и в свободный список для блока добавляется один блок. Если свободный список не пуст, блок в начале списка возвращается вызывающей стороне. Следующий блок в списке становится новым заголовком.

http://publib.boulder.ibm.com/infocenter/systems/index.jsp?topic=/com.ibm.aix.genprogc/doc/genprogc/sys_mem_alloc.htm

Перед указателем стоит размер следующего массива, представляющего собой 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). Это может не повлиять на функциональность приложения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top