Разница между типом массива и массивом, выделенным 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 ответов


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

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

int array[n];

и

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

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

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

int array[n];

тогда вы можете превысить пространство для хранения стека, вызывающее segfault. С другой стороны, куча обычно имеет огромный размер (скажем, столько места, сколько свободно в системе), и так 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]

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

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

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

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


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


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

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


   int array[n];

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

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

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