Разница между типом массива и массивом, выделенным с помощью malloc


сегодня я помогал своему другу с некоторым кодом C, и я нашел какое-то странное поведение, которое я не мог объяснить ему, почему это происходит. У нас был TSV файл со списком целых чисел, с int каждой строки. Первая строка - это количество строк в списке.

У нас также был файл c с очень простым "readfile". Первая строка была прочитана до n, количество строк, затем была инициализация:

int list[n]

и, наконец, цикл for из n с a функции fscanf.

для маленьких n (до ~100.000), все было хорошо. Однако мы обнаружили, что когда n было большим (10^6), произойдет segfault.

наконец, мы изменили инициализацию списка на

int *list = malloc(n*sizeof(int))

и все, когда хорошо, даже при очень больших n.

может кто-нибудь объяснить, почему это произошло? что вызвало segfault с int list[n], который был остановлен, когда мы начали использовать list = malloc(n*sizeof(int))?

9 56

9 ответов:

есть несколько различных частей в игре здесь.

во-первых, разница между объявлением массива

int array[n];

и

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

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

причина, по которой вторая версия работает здесь, - это деталь реализации того, как обычно компилируется C. Как правило, память C разделена на несколько областей, включая стек (для вызовов функций и локальных переменных) и кучу (для malloc объекты ed). Стек обычно имеет гораздо меньший размер, чем куча; обычно это что-то вроде 8 МБ. В результате, если вы попытаетесь выделить огромный массив с

int array[n];

тогда вы можете превысить пространство для хранения стека, вызывающие обработка выхода онлайн / оффлайн. С другой стороны, куча обычно имеет огромный размер (скажем, столько места, сколько свободно в системе), и поэтому mallocing большой объект не вызовет ошибку нехватки памяти.

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

надеюсь, что это помогает!

int list[n]

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

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

int list [n] хранит данные в стеке, в то время как malloc сохраняет их в куче.

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

int list[n] - Это VLA, который выделяет в стеке, а не в куче. Вы не должны освободить его (он автоматически освобождается в конце вызова функции) и он быстро выделяет, но пространство очень ограничено, как вы обнаружили. Вы должны выделить большие значения в куче.

это объявление выделяет память в стеке

    int list[n]

malloc выделяет в куче.

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

см. также этот ответ для получения дополнительной информации

предполагая, что у вас есть типичная реализация в вашей реализации, скорее всего, что:

int list[n]

выделенный список в стеке, где как:

int *list = malloc(n*sizeof(int))

выделение памяти на куче.

в случае стека обычно существует ограничение на то, насколько они могут расти (если они вообще могут расти). В случае кучи все еще есть предел, но это, как правило, в значительной степени и (в целом) ограничено вашим ОЗУ + своп + адресное пространство, которое обычно как минимум на порядок больше, если не больше.

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

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

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

   int array[n];

это пример статически выделенного массива и во время компиляции размер массива будет известен. И массив будет выделен в стеке.

   int *array(malloc(sizeof(int)*n);

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