Почему 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 блоков... и я не понимаю, почему. Я думаю, это может быть причиной для повреждения памяти, которое происходит после выполнения некоторой обработки на выделенных буферах:)

Edit: я видел как я могу получить размер массива из указателя в 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 ответов


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

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

вы должны есть!--6-->

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

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


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


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

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

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

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


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

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

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

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

    array=(short int*)malloc(sizeof(short int) * size);

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

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

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


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


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

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


malloc() будет иметь собственные издержки.

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


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


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

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

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

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

при необходимости

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

размер каждого блока в список, закрепленный ведром, - это размер блока = 2 ведра + 4. Если список в ведре равен null, память выделяется с помощью подпрограммы 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 битным целым числом (не знаю, подписано или без знака)


malloc () будет выделять минимум 17 байт , даже если вы пишете malloc(0), чтобы поддерживать таблицу кучи (хранить информацию о том, насколько большой стек, что такое адрес startung и т. д.)

main()
{
int *p=(int*)malloc(0);
printf("%d\n",p[-1]);//it will give 17 bytes
}

malloc() может выделять непрерывную память, но при вызове malloc () 2 раза и не может ожидать, что выделенная память будет непрерывной, вычитая две переменные указателей...

однако выделенная память является виртуальной памятью, которая является частью реализации ядра, управление памятью (VFS), чтобы быть конкретным. Это не может повлиять на функциональность приложения.