Почему я должен использовать malloc (), когда "char bigchar [1u << 31 - 1];" работает просто отлично? - PullRequest
7 голосов
/ 08 мая 2009

В чем преимущество использования malloc (кроме NULL-возврата при сбое) перед статическими массивами? Следующая программа съест весь мой оперативный памяти и начнет заполнять своп, только если петли не закомментированы. Не вылетает.

...

#include <stdio.h>

unsigned int bigint[ 1u << 29 - 1 ];
unsigned char bigchar[ 1u << 31 - 1 ];

int main (int argc, char **argv) {
  int i;
/*   for (i = 0; i < 1u << 29 - 1; i++) bigint[i] = i; */
/*   for (i = 0; i < 1u << 31 - 1; i++) bigchar[i] = i & 0xFF; */

  getchar();
  return 0;
}

...

После некоторых проб и ошибок я обнаружил, что приведенный выше самый большой статический массив разрешен на моей 32-битной машине Intel с GCC 4.3. Это стандартное ограничение, ограничение компилятора или ограничение компьютера? Видимо, я могу иметь столько их, сколько захочу. Это приведет к ошибке, но только если я попрошу (и попробую использовать) больше, чем даст мне malloc.

Есть ли способ определить, был ли статический массив фактически выделен и безопасен для использования?

РЕДАКТИРОВАТЬ: Меня интересует, почему malloc используется для управления кучей, а не позволяет системе виртуальной памяти обрабатывать ее. Очевидно, я могу увеличить размер массива во много раз по размеру, который мне понадобится, и система виртуальной памяти будет хранить в памяти только то, что нужно. Если я никогда не напишу, например, конец (или начало) этих огромных массивов, тогда программа не использует физическую память. Кроме того, если я могу записывать в каждое местоположение, то что делает malloc, кроме увеличения указателя в куче или поиска предыдущих выделений в том же процессе?

Примечание редактора: 1 << 31 вызывает неопределенное поведение , если int 32-битный, поэтому я изменил вопрос следующим образом: 1u. Цель этого вопроса - спросить о выделении больших статических буферов.

Ответы [ 8 ]

14 голосов
/ 08 мая 2009

Ну, по двум причинам:

  1. Из-за переносимости, поскольку некоторые системы не будут выполнять управление виртуальной памятью за вас.

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

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

Отсюда и причины:

  • Переносимость кода посредством инкапсуляции и стандартизации управления памятью.

  • Повышение личной производительности за счет повторного использования кода.

10 голосов
/ 08 мая 2009

с помощью malloc вы можете увеличивать и уменьшать массив: он становится динамическим, поэтому вы можете выделять именно то, что вам нужно.

5 голосов
/ 08 мая 2009

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

2 голосов
/ 08 мая 2009

Нет способа освободить выделение стека, кроме выхода из области видимости. Поэтому, когда вы фактически используете глобальное распределение, а виртуальная машина выделяет вам реальную жесткую память, она выделяется и будет оставаться там до тех пор, пока ваша программа не закончится. Это означает, что любой процесс будет расти только при использовании виртуальной памяти (функции имеют локальное выделение стека, и они будут «освобождены»).

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

Что сводится к тому, сколько int foo [1 << 29] вы можете иметь. Так как первый занимает всю память (на 32-битной) и будет (допустим, лежат: 0x000000), второй будет преобразован в 0xffffffff или около того. Тогда третий решится на что? То, что 32-битные указатели не могут выразить. (помните, что резервирование стека разрешается частично во время компиляции, частично во время выполнения с помощью смещений, насколько далеко сдвигается стек, когда вы выделяете ту или иную переменную). </p>

Таким образом, ответ в значительной степени таков: если у вас есть int foo [1 << 29], у вас больше не будет разумной глубины функций с другими локальными переменными стека. </p>

2 голосов
/ 08 мая 2009

Вы можете выделить этот гигантский массив в одном контексте, но не в других. Например, если ваш массив является членом структуры и вы хотите передать структуру. В некоторых средах размер структуры не превышает 32 КБ.

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

2 голосов
/ 08 мая 2009

Относительно:

После некоторых проб и ошибок я нашел выше самый большой статический массив разрешено на моей 32-битной машине Intel с GCC 4.3. Это стандарт? лимит, лимит компилятора или машина ограничение

Одна верхняя граница будет зависеть от того, как 4 ГБ (32-разрядное) виртуальное адресное пространство разделено между пользовательским пространством и пространством ядра. Я считаю, что для Linux наиболее распространенная схема секционирования имеет диапазон адресов 3 ГБ для пространства пользователя и диапазон адресов 1 ГБ для пространства ядра. Разбиение настраивается во время сборки ядра, также используются разделения 2ГБ / 2ГБ и 1ГБ / 3ГБ. Когда исполняемый файл загружен, виртуальное адресное пространство должно быть выделено для каждого объекта независимо от того, выделена ли реальная память для его резервного копирования.

1 голос
/ 08 мая 2009

Вы действительно должны избегать этого, если вы не знаете, что делаете. Попробуйте запросить столько памяти, сколько вам нужно. Даже если он не используется или мешает другим программам, он может испортить сам процесс. Для этого есть две причины. Во-первых, в некоторых системах, особенно в 32-битных, это может привести к преждевременному исчерпанию адресного пространства в редких случаях. Кроме того, во многих ядрах есть ограничение на зарезервированную / виртуальную / неиспользуемую память. Если ваша программа запрашивает память в моменты времени выполнения, ядро ​​может завершить процесс, если оно запрашивает зарезервированную память, превышающую этот предел. Я видел программы, которые либо зависали, либо выходили из-за сбоя malloc, потому что они резервируют ГБ памяти, используя только несколько МБ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...