Почему я должен использовать Malloc (), когда «char bigchar [1u << 31 - 1];» работает просто отлично?

StackOverflow https://stackoverflow.com/questions/839291

  •  22-07-2019
  •  | 
  •  

Вопрос

В чем преимущество использования 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.Цель вопроса — задать вопрос о выделении больших статических буферов.

Это было полезно?

Решение

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

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

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

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

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

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

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

Другие советы

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

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

Касательно:

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top