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

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

#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. Я не прав?

Ответы [ 12 ]

30 голосов
/ 10 января 2009

Во-первых, Malloc не дает никаких гарантий, что два последовательных вызова malloc возвращают последовательные указатели.

Во-вторых, в зависимости от вашей конкретной архитектуры применяются разные правила выравнивания; иногда вы можете запросить один байт, но архитектура предпочитает распределения с интервалами в 8 или 4 байта.

В-третьих, malloc нужны некоторые издержки, чтобы сохранить размер выделенного блока и т. Д.

Не делайте предположений о том, что делает malloc после того, что написано в документации!

17 голосов
/ 10 января 2009

Функция malloc всегда выделяет немного больше, чем вы просите, чтобы хранить некоторую бухгалтерскую информацию. В конце концов, когда вы звоните free(), ему нужно знать, насколько велик блок.

Кроме того, обычно malloc реализации округляют запрошенный размер до следующего кратного 8 или 16 или некоторому другому числу округления.

Обновление : реальный ответ на ваш вопрос заключается в использовании вами типа short int. При выполнении арифметики указателя (вычитания) между типизированными указателями C и C ++ возвращают разницу в числе вещей, на которые указывают. Поскольку вы указываете на short int, размер которого составляет два байта, возвращаемое значение составляет половину того, что вы ожидаете.

С другой стороны, malloc всегда выделяет заданное количество байтов , независимо от того, к чему вы приведете результат впоследствии. Попробуйте это:

    array=(short int*)malloc(sizeof(short int) * size);
12 голосов
/ 10 января 2009

Нет никаких гарантий, что два вызова malloc возвращают блоки, точно упакованные вместе - фактически нет никаких гарантий относительно результата, за исключением того, что, если он не равен NULL, он будет указывать на блок, по меньшей мере такой же большой, как и блок просьба.

Внутренне, большинство malloc хранят рабочие данные, чтобы помочь им управлять кучей. Например, эти 8 байтов могут содержать два указателя - один указывает на следующий блок, а другой - на предыдущий блок. Я не знаю, что это за 8 байтов, потому что вы не упомянули, на какой ОС вы работаете, но для malloc вполне нормально использовать некоторую память за кулисами.

Некоторые распределители (например, в Windows) предоставляют библиотечную функцию для определения размера блока по указателю, однако некоторые этого не делают, поскольку это довольно эзотерическая особенность.

9 голосов
/ 10 января 2009

У вас есть ошибка. Вместо:

translatedArray=malloc(4*sizeof(short int));

Вы должны иметь

translatedArray=malloc(4*sizeof(short int*));

Обратите внимание на отсутствующий указатель в вашем коде. Я подозреваю, что именно отсюда и происходит ваше наблюдаемое поведение.


Также обратите внимание, что 0x804a420 - 0x804a018 = 1032, а не 516. Формула translatedArray[i] - translatedArray[i - 1] дает вам число элементов (короткие целые или, проще говоря, короткие) между двумя адресами, а не количество байтов .

5 голосов
/ 10 января 2009

То, что возвращает malloc, зависит от реализации malloc и архитектуры. Как уже говорили другие, вы гарантированно получите по крайней мере запрошенный объем памяти, или NULL. По этой же причине иногда вы можете писать после конца массива и не получать ошибку сегментации. Это потому, что у вас действительно есть действительный доступ к этой памяти, вы просто не знали об этом.

4 голосов
/ 10 января 2009

malloc () обычно реализуется путем разбиения доступной кучи на куски разных размеров. В вашем случае malloc () возвращает 2 последовательных 1024 (или 16) байтовых блока. Упоминаемое вами 8-байтовое пространство используется функцией malloc () для учета информации.

См. Примечания Doug Lea malloc (), чтобы понять, что происходит за кулисами: http://g.oswego.edu/dl/html/malloc.html

3 голосов
/ 10 января 2009

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

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

2 голосов
/ 13 апреля 2009

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

Распределение

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

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

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

При необходимости> 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

2 голосов
/ 10 января 2009

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

1 голос
/ 20 сентября 2015

таким образом, объем выделенной памяти, кажется, имеет 8 дополнительных байтов? malloc() Реализация в вашей системе, кажется, выделяет дополнительные байты для поддержки метаданных, таких как размер раздела кучи, что начинается адрес и т. д. информация

Хотя это зависит от платформы. В моих системах X86 malloc() выделяет минимум 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;
}
...