Можете ли вы привести пример переполнения стека в C++?

Можете ли вы привести пример переполнения стека в C++? Кроме рекурсивного случая:

void foo() { foo(); }

person Nadir SOUALEM    schedule 01.11.2009    source источник
comment
Вы хотите, чтобы я реализовал весь веб-сайт stackoverflow на C++ в своем ответе? Ух ты... :)   -  person dicroce    schedule 01.11.2009
comment
Почему бесконечная рекурсия не является приемлемым ответом?   -  person outis    schedule 01.11.2009
comment
Почему тривиальный случай не является приемлемым ответом?   -  person Drew Dormann    schedule 01.11.2009
comment
Приведенное выше не будет переполняться на приличном компиляторе. Ни стека, ни операций, ничего.   -  person Konrad Rudolph    schedule 01.11.2009


Ответы (12)


Типичным случаем, не связанным с бесконечной рекурсией, является объявление слишком большой автоматической переменной в стеке. Например:

int foo()
{
    int array[1000000];

}
person Tall Jeff    schedule 01.11.2009

См. Переполнение стека — Википедия. Я связался непосредственно с разделом примеров.

person Andrew Hare    schedule 01.11.2009

Вот что может произойти на практике:

int factorial(int x) {
  return x == 0 ? 1 : x * factorial(x-1);
}

Это переполняет стек для отрицательного x. И, как упоминал Фрэнк Крюгер, а также для слишком большого x (но тогда int переполнится первым).

person Thomas    schedule 01.11.2009

Продолжайте пытаться вернуть main, пока не закончится стек?

int main(int argc, char **argv)
{
    return main(argc, argv);
}
person Mark Rushakoff    schedule 01.11.2009
comment
Вызов main() в C++ незаконен. - person Tmdean; 01.11.2009
comment
Я знаю, что это не будет правильно форматироваться, но нет никаких предупреждений с -Wall даже для этой более правильной программы: ‹code›int main(int argc, char **argv) { if (argc == 0){return 0;} else {std::cout ‹‹ argc ‹‹ std::endl; вернуть main(0, argv);} } ‹/code› - person Mark Rushakoff; 01.11.2009
comment
Я имею в виду, что странно, что g++ не выдает никаких предупреждений, когда вы вызываете main; вы действительно правы (из того, что я видел), что стандарт запрещает вызов main. - person Mark Rushakoff; 01.11.2009
comment
@Mark Rushakoff: g++ действительно выдает ошибку, если вы компилируете с флагом -pedantic, хотя в моем случае это дает несколько неправильную ошибку ISO C++ forbids taking address of function '::main' - person Adam Rosenfield; 01.11.2009

Согласно редакции :-)

void ping()
{
  pong();
}

void pong()
{
ping();
}

Кроме того, я считаю, что вы можете получить переполнение стека, если попытаетесь выделить больше места, чем максимальный размер стека потока (1 МБ по умолчанию в VS), поэтому что-то вроде int a[100000]; должно предоставлять исключение.

person Naveen    schedule 01.11.2009

Пример времени компиляции:

template <int N>
struct Factorial {
    enum { value = N * Factorial<N - 1>::value };
};

// ...
{
    int overflow = Factorial<10>::value;
}
person wilhelmtell    schedule 01.11.2009

Не могу поверить, что мы остановились на величайшем примере рекурсии всех времен — факториале!

#include <stdio.h>

double fact(double n) {
    if (n <= 0) return 1;
    else return n * fact(n - 1);
}

int main() {
    printf("fact(5) = %g\n", fact(5));
    printf("fact(10) = %g\n", fact(10));
    printf("fact(100) = %g\n", fact(100));
    printf("fact(1000) = %g\n", fact(1000));
    printf("fact(1000000) = %g\n", fact(1000000));
}

В OS X 10.5.8 с GCC 4.0.1:

$ gcc f.c -o f && ./f
fact(5) = 120
fact(10) = 3.6288e+06
fact(100) = 9.33262e+157
fact(1000) = inf
Segmentation fault

К сожалению, OS X сообщает об «ошибке сегментации» вместо «переполнения стека». Очень жаль.

person Frank Krueger    schedule 01.11.2009

В этом примере показана неконтролируемая рекурсия. В конце концов, стек, выделенный для этого процесса, будет полностью перезаписан экземплярами bar и ret...

int foo( int bar )
{
    int ret = foo( 42 );
    return ret;
}
person dicroce    schedule 01.11.2009

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

#!/usr/bin/env python
import sys

print "void func" + sys.argv[1] + "() { }"
for i in xrange(int(sys.argv[1])-1, -1, -1):
    print "void func" + str(i) + "() { func" + str(i+1) + "(); }"
print "int main() { func0(); return 0; }"

Пример вывода:

$ python recursion.py 5
void func5() { }
void func4() { func5(); }
void func3() { func4(); }
void func2() { func3(); }
void func1() { func2(); }
void func0() { func1(); }
int main() { func0(); return 0; }

Пример использования:

$ python recursion.py 250000 | g++ -x c++ - && ./a.out

По крайней мере, в моей системе стек вызовов выглядит равным 174602, поэтому вам нужно установить аргумент равным recursion.py, чтобы он был больше; компиляция и компоновка программы занимает несколько минут.

person Mark Rushakoff    schedule 01.11.2009

Бесконечная рекурсия:

void infiniteFunction()
{
    infiniteFunction();
}

int main()
{
    infiniteFunction();
    return 0;
}
person Krzysztof Bujniewicz    schedule 01.11.2009

Вы также можете получить переполнение стека, если попытаетесь поместить в стек большие объекты (по значению).

person EricSchaefer    schedule 01.11.2009

person    schedule
comment
+1 чисто и прямо. Это ВСЕ, что вам нужно, чтобы взорвать стек. - person Paul Sasik; 01.11.2009
comment
Можем ли мы сделать его короче? Да мы можем! void _(){_();} ... это почти как Perl ;) - person Thomas; 01.11.2009
comment
Разница в том, что в мире Perl более вероятно, что вы сорвете ваш стек раньше, чем Perl. - person Rob; 01.11.2009
comment
Поскольку эта функция не принимает аргументов, ничего не объявляет и ничего не возвращает, интересно, может ли умный оптимизатор просто превратить это в бесконечный цикл, и в этом случае он не взорвет стек... - person dicroce; 01.11.2009