выделение памяти в C

У меня вопрос относительно порядка распределения памяти. В следующем коде я выделяю в цикле 4 струны. Но когда я печатаю адреса, они, похоже, не выделяются один за другим... Я делаю что-то неправильно или это какой-то механизм защиты, реализованный ОС, чтобы предотвратить возможные переполнения буфера? (Я использую Windows Vista).

спасибо.

 char **stringArr;
 int size=4, i;

 stringArr=(char**)malloc(size*sizeof(char*));
 for (i=0; i<size; i++)
    stringArr[i]=(char*)malloc(10*sizeof(char));

 strcpy(stringArr[0], "abcdefgh");
 strcpy(stringArr[1], "good-luck");
 strcpy(stringArr[2], "mully");
 strcpy(stringArr[3], "stam");

 for (i=0; i<size; i++) {
  printf("%sn", stringArr[i]);
  printf("%d  %unn", &(stringArr[i]), stringArr[i]);
 }

выход:

abcdefgh 9650064 9650128

удачи 9650068 9638624

mully 9650072 9638680

stam 9650076 9638736

7 ответов


обычно, когда вы запрашиваете память через malloc() библиотека времени выполнения C будет округлять размер вашего запроса до некоторого минимального размера. Это гарантирует, что:

  • библиотека времени выполнения имеет место для своей бухгалтерской информации
  • для библиотеки времени выполнения более эффективно управлять выделенными блоками, которые все кратны некоторому размеру (например, 16 байт)

однако это детали реализации, и вы не можете действительно полагайтесь на любое конкретное поведение malloc().


но когда я печатаю адреса, они, похоже, не выделяются один за другим...

Так?

Я делаю что-то неправильно или это какой-то механизм защиты, реализованный ОС, чтобы предотвратить возможные переполнения буфера?

наверное, "ни".

просто из интереса, откуда вы получаете?


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


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


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

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


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

но даже если блоки один за другим, они, скорее всего, не образуют непрерывный блок. Чтобы уменьшить фрагментацию, диспетчер кучи выделяет только блоки определенного размера, например мощность двух (64, 128, 256, 512 и т. д. байты.) Таким образом, если вы зарезервируете 10 байтов для строки, после этого может быть 22 или 54 неиспользуемых байта.

накладные расходы памяти-еще одна причина, по которой не рекомендуется использовать динамическое выделение памяти, если это действительно необходимо. Гораздо проще и безопаснее просто использовать статический массив.


так как вам интересно знать адреса, возвращаемые malloc(), вы должны убедиться, что вы печатаете их должным образом. Под "правильно" я подразумеваю, что вы должны использовать правильный спецификатор формата для printf() для печати адресов. Почему вы используете "%u" для одного и "%d" другого?

вы должны использовать "%p" для печати указателей. Это также один из редких случаев, когда вам нужен бросок в C: потому что printf() является вариационной функцией, компилятор не может сказать что указатели вы передаете в качестве аргумента должны быть типа void * или нет.

кроме того, вы не должны привести возвращаемое значение malloc().

исправив вышеизложенное, программа:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char **stringArr;
    int size=4, i;

    stringArr = malloc(size * sizeof *stringArr);

    for (i=0; i < size; i++)
        stringArr[i] = malloc(10 * sizeof *stringArr[i]);

    strcpy(stringArr[0], "abcdefgh");
    strcpy(stringArr[1], "good-luck");
    strcpy(stringArr[2], "mully");
    strcpy(stringArr[3], "stam");

    for (i=0; i<size; i++) {
        printf("%s\n", stringArr[i]);
        printf("%p %p\n", (void *)(&stringArr[i]), (void *)(stringArr[i]));
    }
    return 0;
}

и я получаю следующий вывод, когда я его запускаю:

abcdefgh
0x100100080 0x1001000a0
good-luck
0x100100088 0x1001000b0
mully
0x100100090 0x1001000c0
stam
0x100100098 0x1001000d0

на моем компьютере, char ** указатели имеют длину 8 байт, поэтому &stringArr[i+1] на 8 байт больше, чем &stringArr[i]. Это гарантируется стандартом: если вы malloc() некоторые пространство, это пространство смежно. Вы выделили место для 4 указателей, и адреса этих четырех указателей находятся рядом друг с другом. Вы можете видеть это более ясно, делая:

printf("%d\n", (int)(&stringArr[1] - &stringArr[0]));

это должно вывести 1.

о последующем malloc()s, так как каждый stringArr[i] получается из отдельной malloc(), реализация может назначить им любые подходящие адреса. В моей реализации, с этим конкретным запуском, адреса все 0x10 байт отдельно.

для вашей реализации это похоже на char ** указатели имеют длину 4 байта.

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