Что вызывает переполнение стека?

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

Я использую сетевой симулятор OPNET, в котором программирую на C. Я думаю, что у меня проблема с большими размерами массивов. Похоже, я нахожусь в каком-то ограничении выделения памяти. Возможно, это связано с OPNET, Windows, памятью моего ноутбука или, скорее всего, языком C. Проблема возникает, когда я пытаюсь использовать вложенные массивы с общим количеством элементов, доходящим до нескольких тысяч целых чисел. Я думаю, что превышаю общий предел выделения памяти, и мне интересно, есть ли способ увеличить это ограничение. Вот точное описание проблемы:

У меня в основном таблица маршрутизации. Назовем его routing_tbl [n], что означает, что я поддерживаю 30 узлов (маршрутизаторов). Теперь для каждого узла в этой таблице я храню информацию. о многих (сотнях) доступных путей в массиве paths [p]. Опять же, для каждого пути в этом массиве я храню список узлов, которые ему принадлежат, в массиве с именем hops [h]. Итак, я использую как минимум целые числа nph в памяти, но эта таблица также содержит другую информацию. В той же функции я также использую другой вложенный массив, который также потребляет почти 40 000 целых чисел. Как только я запускаю симуляцию, она перестает жаловаться на переполнение стека. Это работает, когда я уменьшаю общий размер таблицы маршрутизации. Как вы думаете, что вызывает проблему и как ее решить? Высоко ценил Али


person Community    schedule 19.09.2008    source источник
comment
Не могли бы вы объяснить проблему более подробно, чем ваше текущее решение? Думаю, это побудило бы больше людей прочитать и ответить на ваш вопрос.   -  person Mats Fredriksson    schedule 20.09.2008


Ответы (6)


Это может помочь, если вы разместите какой-нибудь код. Отредактируйте вопрос, включив в него проблемную функцию и ошибку.

Между тем, вот очень общий ответ:

Двумя основными причинами переполнения стека являются: 1) рекурсивная функция или 2) выделение большого количества локальных переменных.

Рекурсия

если ваша функция вызывает сама себя, например:

int recurse(int number) {

    return (recurse(number));
}

Поскольку локальные переменные и аргументы функций хранятся в стеке, они заполнят стек и вызовут переполнение стека.

Большие локальные переменные

Если вы попытаетесь выделить большой массив локальных переменных, вы можете переполнить стек одним простым движением. Подобная функция может вызвать проблему:

void hugeStack (void) {

    unsigned long long reallyBig[100000000][1000000000];

    ...
}

На этот есть довольно подробный ответ. аналогичный вопрос.

person Andrew Johnson    schedule 19.09.2008
comment
Технически функция рекурсии будет оптимизирована по хвосту до простого бесконечного цикла, но давайте не будем слишком увлекаться этим примером :) - person Andrew Johnson; 20.09.2008
comment
Судя по описанию, проблема заключается в том, что в стек помещено слишком много данных, а не рекурсия. Бьюсь об заклад, размещение переменных в куче (с помощью malloc) решит проблему. - person Jon Ericson; 20.09.2008
comment
Да, сначала мне ответили именно так, но потом я подумал, что он может пересекать свою сеть с какой-то функцией. Я подумал, что просто добавлю базовый справочный ответ, и любой, кто бродит по этому пути, сможет получить подсказку. Кто знает, возможно, этот нуб никогда не вернется :) - person Andrew Johnson; 20.09.2008

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

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

person user11318    schedule 19.09.2008
comment
Как передать массив в стек в C? - person Alexander; 20.09.2008

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

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

Есть ли причина, по которой вы не можете использовать отладчик в этой программе?

person tkerwin    schedule 19.09.2008
comment
Я знаю, придирчиво, но глубины не велики, они глубоки. Прости. - person Cyberherbalist; 20.09.2008

Это зависит от того, где вы объявили переменную.

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

Динамически выделяемый объект (то есть тот, который находится в куче) ограничен объемом доступной памяти. Это свойство ОС (и технически может увеличить объем физической памяти, если у вас интеллектуальная ОС).

person Martin York    schedule 20.09.2008

Многие операционные системы динамически расширяют стек по мере его увеличения. Когда вы начинаете запись в адрес памяти, который находится сразу за пределами стека, ОС предполагает, что ваш стек только что увеличился, и выделяет ему дополнительную страницу (обычно 4096Kib на x86 - ровно 1024 int).

Проблема в том, что на x86 (и некоторых других архитектурах) стек растет вниз, а массивы C растут вверх. Это означает, что если вы обращаетесь к началу большого массива, вы будете получать доступ к памяти, находящейся на расстоянии более одной страницы от края стека.

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

Возможно, вам удастся найти некоторые функции API ОС для принудительного выделения стека или прагмы / флаги компилятора. Я не уверен, как это можно сделать переносимо, за исключением, конечно, использования malloc () и free ()!

person Artelius    schedule 29.10.2008

Вы вряд ли столкнетесь с переполнением стека при использовании скомпилированного без потоков C, если только вы не сделаете что-то особенно вопиющее, например, неконтролируемую рекурсию или космическую утечку памяти. Однако ваш симулятор, вероятно, имеет пакет потоковой обработки, который налагает ограничения на размер стека. Когда вы запускаете новый поток, он выделяет часть памяти для стека для этого потока. Вероятно, есть параметр, который вы можете где-то установить, который устанавливает размер стека по умолчанию, или может быть способ динамического увеличения стека. Например, в pthreads есть функция pthread_attr_setstacksize (), которую вы вызываете перед запуском нового потока, чтобы установить его размер. Ваш симулятор может использовать или не использовать pthreads. Проконсультируйтесь со справочной документацией по симулятору.

person mxg    schedule 20.09.2008