Programming

스택 풀기 란 무엇입니까?

procodes 2020. 5. 15. 21:10
반응형

스택 풀기 란 무엇입니까?


스택 풀기 란 무엇입니까? 검색했지만 깨달은 답변을 찾을 수 없습니다!


스택 해제는 일반적으로 예외 처리와 관련하여 설명됩니다. 예를 들면 다음과 같습니다.

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입니다). 이는 컴파일러가 자동 (스택) 변수의 소멸자에 대한 호출을 삽입하여 수행됩니다.

이제 이것은 C ++에서 메모리, 데이터베이스 연결, 열린 파일 디스크립터 등과 같은 리소스를 관리하는 데 도움 되는 RAII 라는 기술을 이끌어내는 매우 강력한 개념 입니다.

이제 우리는 예외 안전 보장 을 제공 할 수 있습니다 .


이 모든 것은 C ++과 관련이 있습니다.

정의 : 힙 메모리에 객체를 할당하는 것이 아니라 스택에 객체를 정적으로 만들고 함수 호출을 수행 할 때 "스택"됩니다.

범위 ( {및로 구분 된 })가 종료 return XXX;되면 (를 사용 하여 범위의 끝에 도달하거나 예외를 throw하여) 해당 범위 내의 모든 항목이 삭제됩니다 (소멸자가 모든 것을 호출 함). 로컬 객체를 파괴하고 소멸자를 호출하는 이러한 프로세스를 스택 해제라고합니다.

스택 해제와 관련하여 다음과 같은 문제가 있습니다.

  1. 메모리 누수 방지 (로컬 객체에 의해 관리되지 않고 소멸자에서 정리 된 동적으로 할당 된 것은 누출 됨)-Nikolai가 참조한 RAII boost :: scoped_ptr에 대한 문서 또는 boost :: mutex 를 사용하는 예제 :: scoped_lock .

  2. 프로그램 일관성 : C ++ 사양에 따르면 기존 예외를 처리하기 전에 예외를 throw해서는 안됩니다. 이것은 스택 해제 프로세스가 예외를 발생시키지 않아야 함을 의미합니다 (소멸자를 던지지 않도록 보장 된 코드 만 사용하고 try {으로 소멸자의 모든 것을 둘러싸십시오 } catch(...) {}).

스택 해제 중에 소멸자가 예외를 throw하면 정의되지 않은 동작 이 발생하여 프로그램이 예기치 않게 종료되거나 (가장 일반적인 동작) 유니버스가 종료됩니다 (이론적으로 가능하지만 실제로는 아직 관찰되지 않음).


일반적으로 스택 "언 와인드 (unwind)"는 함수 호출의 끝과 그에 따른 스택의 팝핑과 거의 동의어입니다.

그러나 특히 C ++의 경우 스택 해제는 C ++이 코드 블록이 시작된 이후 할당 된 객체에 대해 소멸자를 호출하는 방법과 관련이 있습니다. 블록 내에서 생성 된 개체는 할당 순서와 반대로 할당이 해제됩니다.


스택 해제는 주로 C ++ 개념으로, 범위가 종료 될 때 (일반적으로 또는 예외를 통해) 스택 할당 된 객체가 소멸되는 방식을 처리합니다.

이 코드 조각이 있다고 가정 해보십시오.

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

글을 아직 읽었는지 모르겠지만 , 콜 스택에 관한 Wikipedia의 기사 에는 적절한 설명이 있습니다.

풀기 :

Returning from the called function will pop the top frame off of the stack, perhaps leaving a return value. The more general act of popping one or more frames off the stack to resume execution elsewhere in the program is called stack unwinding and must be performed when non-local control structures are used, such as those used for exception handling. In this case, the stack frame of a function contains one or more entries specifying exception handlers. When an exception is thrown, the stack is unwound until a handler is found that is prepared to handle (catch) the type of the thrown exception.

Some languages have other control structures that require general unwinding. Pascal allows a global goto statement to transfer control out of a nested function and into a previously invoked outer function. This operation requires the stack to be unwound, removing as many stack frames as necessary to restore the proper context to transfer control to the target statement within the enclosing outer function. Similarly, C has the setjmp and longjmp functions that act as non-local gotos. Common Lisp allows control of what happens when the stack is unwound by using the unwind-protect special operator.

When applying a continuation, the stack is (logically) unwound and then rewound with the stack of the continuation. This is not the only way to implement continuations; for example, using multiple, explicit stacks, application of a continuation can simply activate its stack and wind a value to be passed. The Scheme programming language allows arbitrary thunks to be executed in specified points on "unwinding" or "rewinding" of the control stack when a continuation is invoked.

Inspection[edit]


I read a blog post that helped me to understand.

What is stack unwinding?

In any language that supports recursive functions (ie. pretty much everything except Fortran 77 and Brainf*ck) the language runtime keeps a stack of what functions are currently executing. Stack unwinding is a way of inspecting, and possibly modifying, that stack.

Why would you want to do that?

The answer may seem obvious, but there are several related, yet subtly different, situations where unwinding is useful or necessary:

  1. As a runtime control-flow mechanism (C++ exceptions, C longjmp(), etc).
  2. In a debugger, to show the user the stack.
  3. In a profiler, to take a sample of the stack.
  4. From the program itself (like from a crash handler to show the stack).

These have subtly different requirements. Some of these are performance-critical, some are not. Some require the ability to reconstruct registers from outer frame, some do not. But we'll get into all that in a second.

You can found the full post here.


Everyone has talked about the exception handling in C++. But,I think there is another connotation for stack unwinding and that is related to debugging. A debugger has to do stack unwinding whenever it is supposed to go to a frame previous to the current frame. However, this is sort of virtual unwinding as it needs to rewind when it comes back to current frame. The example for this could be up/down/bt commands in gdb.


IMO, the given below diagram in this article beautifully explains the effect of stack unwinding on the route of next instruction (to be executed once an exception is thrown which is uncaught):

enter image description here

In the pic:

  • Top one is a normal call execution (with no exception thrown).
  • Bottom one when an exception is thrown.

In the second case, when an exception occurs, the function call stack is linearly searched for the exception handler. The search ends at the function with exception handler i.e. main() with enclosing try-catch block, but not before removing all the entries before it from the function call stack.


C++ runtime destructs all automatic variables created between between throw & catch. In this simple example below f1() throws and main() catches, in between objects of type B and A are created on the stack in that order. When f1() throws, B and A's destructors are called.

#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;
}

The output of this program will be

B's dtor
A's dtor

This is because the program's callstack when f1() throws looks like

f1()
f()
main()

So, when f1() is popped, automatic variable b gets destroyed, and then when f() is popped automatic variable a gets destroyed.

Hope this helps, happy coding!


When an exception is thrown and control passes from a try block to a handler, the C++ run time calls destructors for all automatic objects constructed since the beginning of the try block. This process is called stack unwinding. The automatic objects are destroyed in reverse order of their construction. (Automatic objects are local objects that have been declared auto or register, or not declared static or extern. An automatic object x is deleted whenever the program exits the block in which x is declared.)

If an exception is thrown during construction of an object consisting of subobjects or array elements, destructors are only called for those subobjects or array elements successfully constructed before the exception was thrown. A destructor for a local static object will only be called if the object was successfully constructed.


In Java stack unwiding or unwounding isn't very important (with garbage collector). In many exception handling papers I saw this concept (stack unwinding), in special those writters deals with exception handling in C or C++. with try catch blocks we shouln't forget: free stack from all objects after local blocks.

참고URL : https://stackoverflow.com/questions/2331316/what-is-stack-unwinding

반응형