Что такое стека?



Что такое стека? Перерыл но не смог найти ясный ответ!

576   11  

11 ответов:

о размотке стека обычно говорят в связи с обработкой исключений. Вот пример:

void func( int x )
{
    char* pleak = new char[1024]; // might be lost => memory leak
    std::string s( "hello world" ); // will be properly destructed

    if ( x ) throw std::runtime_error( "boom" );

    delete [] pleak; // will only get here if x == 0. if x!=0, throw exception
}

int main()
{
    try
    {
        func( 10 );
    }
    catch ( const std::exception& e )
    {
        return 1;
    }

    return 0;
}

здесь выделена память для pleak будет потеряно, если возникнет исключение, в то время как память выделена для s будет правильно выпущен std::string деструктор в любом случае. Объекты, выделенные в стеке, "разматываются" при выходе из области (здесь область действия функции func.) Это делается компилятором, вставляющим вызовы деструкторов автоматические (стековые) переменные.

теперь это очень мощная концепция, ведущая к технике, называемой RAII, что составляет Приобретение Ресурсов Является Инициализацией, что помогает нам управлять такими ресурсами, как память, соединения с базами данных, открытые файловые дескрипторы и т. д. в языке C++.

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

все это относится к C++:

определение: Когда вы создаете объекты статически (в стеке, а не выделяете их в памяти кучи) и выполняете вызовы функций, они "складываются".

когда область (что-нибудь, разделенное { и }) закрывается (с помощью return XXX;, достигая конца области или бросая исключение) все в пределах этой области уничтожается (деструкторы вызываются для всего). этот процесс уничтожение локальных объектов и вызов деструкторов называется размоткой стека.

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

  1. предотвращение утечек памяти (все динамически выделенное, что не управляется локальным объектом и очищается в деструкторе, будет утечка) - см. RAII называют Николая и документация для boost:: scoped_ptr или такой пример использования boost:: mutex:: scoped_lock.

  2. согласованность программы: в спецификациях C++ указано, что вы никогда не должны создавать исключение до обработки любого существующего исключения. Это значит, что процесс размотки стека никогда не должен вызывать исключение (либо использовать только код гарантированно не бросать в деструкторы, либо окружать все в деструкторах с try { и } catch(...) {}).

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

В общем смысле, стек "unwind" в значительной степени синонимичен концу вызова функции и последующему появлению стека.

однако, особенно в случае C++, разматывание стека связано с тем, как C++ вызывает деструкторы для объектов, выделенных с момента запуска любого блока кода. Объекты, созданные внутри блока, освобождаются в обратном порядке их размещения.

Stack unwinding-это в основном концепция C++, касающаяся того, как объекты, выделенные стеком, уничтожаются при выходе из его области (обычно или через исключение).

скажем, у вас есть этот фрагмент кода:

void hw() {
    string hello("Hello, ");
    string world("world!\n");
    cout << hello << world;
} // at this point, "world" is destroyed, followed by "hello"

Я не знаю, читали ли вы это еще, но статья Википедии о стеке вызовов есть достойное объяснение.

разматывать:

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

некоторые языки имеют другие структуры управления, которые требуют общего разматывать. Паскаль позволяет глобальный goto для передачи управления из вложенных функция и в ранее вызванную внешнюю функцию. Эта операция требует, чтобы стек был размотан, удаляя столько кадров стека, сколько необходимо, чтобы восстановить надлежащий контекст для передачи управления целевому оператору в заключающей внешней функции. Аналогично, C имеет функции setjmp и longjmp, которые действуют как нелокальные gotos. Common Lisp позволяет управлять тем, что происходит, когда стек разматывается с помощью специального оператора unwind-protect.

при применении продолжение, стек (логически) разматывается, а затем перематывается со стеком продолжения. Это не единственный способ реализации продолжений; например, используя несколько явных стеков, применение продолжения может просто активировать его стек и обмотать значение, которое будет передано. Язык программирования схемы позволяет выполнять произвольные прогибы в заданных точках при "размотке" или "перемотке" стека управления при продолжении вызванный.

инспекция [edit]

Я прочитал сообщение в блоге, которое помогло мне понять.

что такое стека?

в любом языке, который поддерживает рекурсивные функции (т. е. довольно много все, кроме Fortran 77 и Brainf*ck) языковая среда выполнения сохраняет стек, какие функции выполняют. Стек разматывать способ проверки и, возможно, изменения этого стека.

зачем вы хотите это сделать?

ответ может показаться очевидным, но есть несколько, но тонко различные ситуации, когда разматывание полезно или необходимо:

  1. как механизм управления потоком времени выполнения (исключения C++, C longjmp () и т. д.).
  2. в отладчике, чтобы показать пользователю стека.
  3. в профилировщике, чтобы взять образец стека.
  4. из самой программы (например, из обработчика сбоев, чтобы показать стек).

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

вы можете найти всю статью здесь.

все говорили об обработке исключений в C++. Но, я думаю, что есть еще одна коннотация для размотки стека, и это связано с отладкой. Отладчик должен выполнять размотку стека всякий раз, когда он должен перейти к кадру, предшествующему текущему кадру. Однако это своего рода виртуальная размотка, так как она должна перематываться, когда она возвращается к текущему кадру. Примером для этого могут быть команды up/down/bt в gdb.

C++ runtime уничтожает все автоматические переменные, созданные между throw & catch. В этом простом примере ниже F1() броски и main () уловы, между объектами типа B и A создаются в стеке в этом порядке. Когда F1 () бросает, деструкторы B и A вызываются.

#include <iostream>
using namespace std;

class A
{
    public:
       ~A() { cout << "A's dtor" << endl; }
};

class B
{
    public:
       ~B() { cout << "B's dtor" << endl; }
};

void f1()
{
    B b;
    throw (100);
}

void f()
{
    A a;
    f1();
}

int main()
{
    try
    {
        f();
    }
    catch (int num)
    {
        cout << "Caught exception: " << num << endl;
    }

    return 0;
}

выход этой программы будет

B's dtor
A's dtor

Это потому, что стек вызовов программы, когда F1 () бросает выглядит как

f1()
f()
main()

Итак, когда F1 () выскочил, автоматическая переменная b уничтожается, а затем, когда F() выскакивает, автоматическая переменная a уничтожается.

надеюсь, что это поможет, счастливое кодирование!

ИМО, приведенная ниже диаграмма в этом статьи красиво объясняет эффект разматывания стека на маршруте следующей инструкции (выполняется после того, как выбрасывается исключение, которое не перехвачено):

enter image description here

На фото:

  • Top one-это обычное выполнение вызова (без исключения).
  • Нижний при возникновении исключения.

во втором случае, когда возникает исключение, стек вызовов функций линейно ищет обработчик исключений. Поиск заканчивается на функции с обработчиком исключений, т. е. main() с приложением try-catch блок, но не перед удаление всех записей перед ним из стека вызовов функций.

когда возникает исключение и управление переходит от блока try к обработчику, время выполнения C++ вызывает деструкторы для всех автоматических объектов, построенных с начала блока try. Этот процесс называется размоткой стека. Автоматические объекты уничтожаются в обратном порядке их строительства. (Автоматические объекты-это локальные объекты, которые были объявлены автоматически или зарегистрированы, или не объявлены статическими или внешними. Автоматический объект x удаляется всякий раз, когда программа выходит из блока в которой объявлен x.)

Если исключение создается во время построения объекта, состоящего из подобъектов или элементов массива, деструкторы вызываются только для тех подобъектов или элементов массива, которые были успешно созданы до создания исключения. Деструктор для локального статического объекта будет вызываться только в том случае, если объект был успешно создан.

в стеке Java unwiding или unwounding не очень важно (с сборщиком мусора). Во многих работах по обработке исключений я видел эту концепцию (разматывание стека), в частности, эти писатели занимаются обработкой исключений в C или c++. с try catch блоки мы не должны забывать: свободный стек из всех объектов после локальных блоков.

Comments

    Ничего не найдено.