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

60

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

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

<code>int list[n]
</code>

и, наконец, цикл для n с помощью fscanf.

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

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

<code>int *list = malloc(n*sizeof(int))
</code>

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

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

Ваш Ответ

9   ответов
136

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

int array[n];

а такж

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

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

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

int array[n];

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

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

Надеюсь это поможет

Очень разъясняющий ответ ... спасибо! Jorge Leitão
Отличный ответ! Мне было интересно, есть ли разница в скорости? Francesco Boccardo
Из-за влияния локальности ссылок, я подозреваю, что выделенный в стек массив быстрее доступен, иmallocам по себе @ намного медленнее, чем просто удар по указателю стека. Но на самом деле лучше использовать любой подход, более подходящий для поставленной задачи. templatetypedef
8
int list[n]

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

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

«Использование памяти в стеке намного быстрее, чем альтернатива», что вы подразумеваете под «распределением» или «доступом»? AFAIK, распределение стека происходит намного быстрее, но верно ли оно для доступа (чтение / запись)? Благодарност dragonxlwang
2

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

1

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

1

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

    int list[n]

malloc размещается в куче.

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

Смотрите также этот ответ для дальнейшей информации

1

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

int list[n]

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

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

выделенная память в вашей куче.

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

0

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

0

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

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

0
   int array[n];

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

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

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

Похожие вопросы