В чем преимущество malloc?

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

подобно

 int *lis;
 lis = (int*) malloc ( sizeof( int ) * n );

 /* Initialize LIS values for all indexes */
 for ( i = 0; i < n; i++ )
 lis[i] = 1;

мы могли бы использовать обычный массив.

Ну, я не совсем понимаю, как работает malloc, что на самом деле делает. Поэтому объяснение их было бы более полезным для меня.

И предположим, что мы заменим sizeof(int) * n просто n в приведенном выше коде, а затем попробуйте сохранить целочисленные значения, с какими проблемами я могу столкнуться? И есть ли способ напечатать значения, хранящиеся в переменной, непосредственно из выделенного пространства памяти, например, здесь это lis?

6 ответов

Решение

Ваш вопрос скорее сравнивает динамически размещенные массивы в стиле C с массивами переменной длины, что означает, что это может быть тем, что вы ищете: почему массивы переменной длины не являются частью стандарта C++?

Однако тег C++ дает окончательный ответ: использовать std::vector объект вместо

Пока это возможно, избегайте динамического выделения и ответственности за уродливое управление памятью ~> вместо этого попробуйте воспользоваться объектами с автоматической продолжительностью хранения. Другое интересное чтение может быть: Понимание значения термина и концепции - RAII (Приобретение ресурсов - Инициализация)


"И предположим, что мы заменим sizeof(int) * n просто n в приведенном выше коде, а затем попробуйте сохранить целочисленные значения, с какими проблемами я могу столкнуться? "
- Если вы все еще считаете n чтобы быть количеством целых чисел, которое можно сохранить в этом массиве, вы, скорее всего, будете испытывать неопределенное поведение.

Ответ прост. Локальные массивы1 расположены в вашем стеке, что является небольшим предварительно выделенным объемом памяти для вашей программы. Помимо пары тысяч данных, вы не можете сделать многое в стеке. Для больших объемов данных вам нужно выделить память из стека.

Это то, что malloc делает.

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

Чтение значений из памяти (например, для печати) аналогично чтению из массива. Например printf("%d", list[5]);,

До C99 (я знаю, что вопрос помечен C++, но, вероятно, вы изучаете C-compiled-in-C++), была и другая причина. Не было никакого способа получить массив переменной длины в стеке. (Даже сейчас массивы переменной длины в стеке не так полезны, так как стек небольшой). Вот почему для переменного объема памяти, вам нужно было malloc Функция для выделения памяти настолько большой, насколько вам нужно, размер которой определяется во время выполнения.

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

Пример mallocобъекты FILEs. Как только вы позвоните fopenструктура, которая содержит данные, относящиеся к открытому файлу, динамически распределяется с использованием malloc и возвращается как указатель (FILE *).


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

Более фундаментально, я думаю, кроме проблем стека против кучи и переменных против констант (и того факта, что вы не должны использовать malloc() в C++, для начала), заключается в том, что локальный массив перестает существовать при выходе из функции. Если вы вернете указатель на него, этот указатель станет бесполезным, как только получатель получит его, тогда как память динамически выделяется с помощью malloc() или же new все еще будет в силе. Вы не могли бы реализовать такую ​​функцию, как strdup() например, используя локальный массив, или разумно реализовать связанный список представлений или дерево.

Я полагаю, вы спрашиваете, какова цель с maloc()Скажем, вы хотите получить данные от пользователя и выделить массив такого размера:

int n;
scanf("%d",&n);
int arr[n];

Это не удастся, потому что n не доступен во время компиляции. Сейчас начнется malloc()Вы можете написать:

int n;
scanf("%d",&n);
int* arr = malloc(sizeof(int)*n);

На самом деле malloc() динамически распределять память в области кучи

мы могли бы использовать обычный массив

В C++ (по крайней мере, в этом году) массивы имеют статический размер; поэтому создаем его из значения времени выполнения:

int lis[n];

не допускается. Некоторые компиляторы допускают это как нестандартное расширение, и оно должно стать стандартом в следующем году; но на данный момент, если нам нужен массив динамического размера, мы должны распределить его динамически.

В C это означало бы возиться с malloc; но вы спрашиваете о C++, так что вы хотите

std::vector<int> lis(n, 1);

выделить массив размера n содержащий int значения инициализированы до 1.

(Если хотите, вы можете выделить массив new int[n]и не забудьте освободить его delete [] lis когда вы закончите, и будьте особенно осторожны, чтобы не просочиться, если выдается исключение; но жизнь слишком коротка для этой ерунды.)

Ну, я не совсем понимаю, как работает malloc, что на самом деле делает. Поэтому объяснение их было бы более полезным для меня.

malloc в С и new в C++ выделяют постоянную память из "свободного хранилища". В отличие от памяти для локальных переменных, которая освобождается автоматически, когда переменная выходит из области видимости, она сохраняется до тех пор, пока вы не освободите ее явно (free в С, delete в C++). Это необходимо, если вам нужен массив, чтобы пережить текущий вызов функции. Также неплохо, если массив очень большой: локальные переменные (как правило) хранятся в стеке с ограниченным размером. Если это переполнится, программа потерпит крах или иным образом пойдет не так. (И в текущем стандарте C++ это необходимо, если размер не является константой времени компиляции).

И предположим, что мы заменим sizeof(int) * n просто n в приведенном выше коде, а затем попробуйте сохранить целочисленные значения, с какими проблемами я могу столкнуться?

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

И есть ли способ напечатать значения, хранящиеся в переменной, непосредственно из выделенного пространства памяти, например, здесь это lis?

Вы имеете в виду что-то вроде этого?

for (i = 0; i < len; ++i) std::cout << lis[i] << '\n';

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

  • Размер статического массива устанавливает жесткий верхний предел количества данных, которые программа может обрабатывать в любой момент времени без перекомпиляции. Если вы когда-нибудь пытались сделать что-то сложное в TeX и получили сообщение "Превышена емкость, извините", вот почему.
  • Операционная система (как она была) должна была зарезервировать место для статического массива сразу, независимо от того, будет ли он использоваться. Это явление привело к "чрезмерной загрузке", при которой ОС делает вид, что выделила всю память, которую вы, возможно, захотите, но затем убивает ваш процесс, если вы на самом деле пытаетесь использовать больше, чем доступно. Зачем кому-то этого хотеть? И все же он был раскручен как функция в коммерческом Unix середины 90-х годов, потому что это означало, что гигантские симуляции FORTRAN, которые потенциально нуждались в гораздо большем объеме памяти, чем ваша маленькая компьютерная станция Sun, могли быть протестированы на небольших размерах экземпляра без проблем. (Предположительно, вы бы запустили большой экземпляр на Cray, где на самом деле было достаточно памяти, чтобы справиться.)
  • Динамические распределители памяти трудно реализовать хорошо. Посмотрите на бумагу из джемлока, чтобы понять, насколько она может быть волосатой. (Если вам нужна автоматическая сборка мусора, все становится еще сложнее.) Это именно то, что вы хотите, чтобы гуру однажды написал код для всеобщей пользы.

Так что в настоящее время даже довольно скромные встроенные среды дают вам своего рода динамический распределитель.

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

Однако, однако, если не использовать динамическое выделение памяти, когда это действительно необходимо, это может привести к собственным проблемам, таким как наложение жестких верхних ограничений на длину строк или включение невмешательства в ваш API (сравните gethostbyname в getaddrinfo).

Поэтому вы должны тщательно обдумать это.

Другие вопросы по тегам