распределение памяти в C - PullRequest
       25

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

6 голосов
/ 25 января 2010

У меня есть вопрос относительно порядка выделения памяти. В следующем коде я размещаю в цикле 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("%s\n", stringArr[i]);
  printf("%d  %u\n\n", &(stringArr[i]), stringArr[i]);
 }

Выход:

ABCDEFGH 9650064 9650128

хороший удачи 9650068 9638624

Муллы 9650072 9638680

ст 9650076 9638736

Ответы [ 7 ]

7 голосов
/ 25 января 2010

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

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

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

4 голосов
/ 25 января 2010

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

4 голосов
/ 25 января 2010

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

4 голосов
/ 25 января 2010

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

Так

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

Вероятно, "ни один".

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

3 голосов
/ 25 января 2010

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

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

2 голосов
/ 25 января 2010

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

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

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

1 голос
/ 25 января 2010

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

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

Кроме того, вы не должны приводить возвращаемое значение 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() с, поскольку каждый stringArr[i] получен из отдельного malloc(), реализация может назначать им любые подходящие адреса. В моей реализации при этом конкретном запуске все адреса разделяются на 0x10 байт.

Для вашей реализации кажется, что char ** указатели имеют длину 4 байта.

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

...