스택 풀기 란 무엇입니까?


193

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


76
그가 그것이 무엇인지 모른다면, C와 C ++에 대해 동일하지 않다는 것을 어떻게 알 수 있습니까?
dreamlax

@dreamlax : 그렇다면 C & C ++에서 "스택 풀기"의 개념은 어떻게 다른가요?
소멸자

2
@PravasiMeet : C에는 예외 처리가 없으므로 스택 해제는 매우 간단하지만 C ++에서는 예외가 발생하거나 함수가 종료되면 스택 해제는 자동 저장 기간으로 C ++ 객체를 파괴합니다.
dreamlax

답변:


150

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

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 라는 기술 ( 리소스 획득이 초기화 임)로 이어지는 매우 강력한 개념 입니다.

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


정말 깨달았습니다! 그래서 나는 이것을 얻는다 : 스택이 팝 된 시간에 블록을 떠나는 동안 내 프로세스가 예기치 않게 중단되면 예외 처리기 코드 이후의 코드가 전혀 실행되지 않고 메모리 누수가 발생할 수 있습니다. 힙 손상 등
Rajendra Uppal 3:25에

15
프로그램이 "충돌"하는 경우 (즉 , 오류로 인해 종료 되는 경우) 메모리가 종료 될 때 해제되므로 메모리 누수 또는 힙 손상은 관련이 없습니다.
Tyler McHenry

1
바로 그거죠. 감사. 나는 오늘 약간 난독증이되고 있습니다.
Nikolai Fetissov

11
@TylerMcHenry :이 표준은 종료시 리소스 또는 메모리가 릴리스되는 것을 보증하지 않습니다. 그러나 대부분의 OS는 그렇게합니다.
Mooing Duck

3
delete [] pleak;x == 0 인 경우에만 도달합니다.
Jib

71

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

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

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

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

  1. (아무것도 동적으로 유출 될 로컬 객체에 의해 관리되고 소멸자에서 정리되지 않도록 할당) 메모리 누수를 방지 - RAII는 참조 라고 니콜라이에 의해, 그리고 부스트 :: scoped_ptr를 설명서 또는 사용의 예 부스트 :: 뮤텍스를 :: scoped_lock .

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

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


2
반대로. gotos는 남용해서는 안되지만 MSVC에서 스택 풀림을 유발합니다 (GCC가 아니라 확장 일 가능성이 있음). setjmp와 longjmp는 약간의 유연성을 가지고 크로스 플랫폼 방식으로이를 수행합니다.
Patrick Niedzielski

10
방금 gcc로 이것을 테스트했으며 코드 블록에서 벗어날 때 소멸자를 올바르게 호출합니다. 참조 stackoverflow.com/questions/334780/...를 - 링크에서 언급 한 바와 같이,이 표준의 일부뿐만 아니라입니다.
Damyan

1
Nikolai 's, jrista 's 및 귀하의 답변을이 순서대로 읽으면 이제 의미가 있습니다!
n611x007

@sashoalm 7 년 후 게시물을 편집해야한다고 생각하십니까?
David Hoelzer

41

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

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


4
try블록 에는 특별한 것이 없습니다 . 모든 블록에 할당 된 스택 객체 try는 블록이 종료 될 때 풀릴 수 있습니다.
Chris Jester-Young

많은 C ++ 코딩을 한 이후로 오래되었습니다. 나는 그 답을 녹슨 깊이에서 파헤쳐 야했다. ; P
jrista 2012

걱정마 모든 사람은 때때로 "나쁜"것을 가지고 있습니다.
bitc

13

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

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

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

이것은 모든 블록에 적용됩니까? {// 일부 로컬 객체 만있는 경우}
Rajendra Uppal

@Rajendra : 예. 익명 블록은 범위의 영역을 정의하므로 계산됩니다.
Michael Myers

12

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

풀기 :

호출 된 함수에서 리턴하면 스택에서 맨 위 프레임이 튀어 나와 리턴 값이 남습니다. 프로그램의 다른 곳에서 실행을 재개하기 위해 스택에서 하나 이상의 프레임을 팝하는보다 일반적인 동작을 스택 해제 라고합니다. 하며 예외 처리에 사용되는 것과 같이 로컬이 아닌 제어 구조를 사용할 때 수행해야합니다. 이 경우 함수의 스택 프레임에는 예외 핸들러를 지정하는 하나 이상의 항목이 포함됩니다. 예외가 발생하면 처리 된 예외 유형을 처리 (캐치) 할 수있는 핸들러가 발견 될 때까지 스택이 풀립니다.

일부 언어에는 일반적인 풀기를 필요로하는 다른 제어 구조가 있습니다. 파스칼을 사용하면 전역 goto 문이 중첩 된 함수에서 이전에 호출 된 외부 함수로 제어를 전송할 수 있습니다. 이 작업을 수행하려면 스택을 풀어야하는데, 외부 컨텍스트 내에서 대상 명령문으로 제어를 전송하기 위해 적절한 컨텍스트를 복원하는 데 필요한만큼의 스택 프레임을 제거해야합니다. 마찬가지로 C에는 로컬이 아닌 gotos로 작동하는 setjmp 및 longjmp 함수가 있습니다. Common Lisp을 사용하면 unwind-protect 특수 연산자를 사용하여 스택이 풀릴 때 발생하는 상황을 제어 할 수 있습니다.

연속을 적용 할 때 스택은 (논리적으로) 풀린 다음 연속 스택으로 다시 감습니다. 이것이 연속을 구현하는 유일한 방법은 아닙니다. 예를 들어, 여러 개의 명시 적 스택을 사용하여 연속 적용은 단순히 스택을 활성화하고 전달할 값을 감을 수 있습니다. Scheme 프로그래밍 언어는 연속이 호출 될 때 제어 스택의 "풀기"또는 "되감기"의 지정된 지점에서 임의의 썽크가 실행될 수 있도록합니다.

검사 [편집]


9

이해하는 데 도움이되는 블로그 게시물을 읽었습니다.

스택 풀기 란 무엇입니까?

재귀 함수를 지원하는 모든 언어 (예 : Fortran 77 및 Brainf * ck를 제외한 거의 모든 언어)에서 언어 런타임은 현재 실행중인 함수의 스택을 유지합니다. 스택 해제는 해당 스택을 검사하고 수정하는 방법입니다.

왜 그렇게 하시겠습니까?

답은 명백해 보일 수 있지만 풀리는 것이 유용하거나 필요한 몇 가지 관련되어 있지만 미묘하게 다른 상황이 있습니다.

  1. 런타임 제어 흐름 메커니즘으로 (C ++ 예외, C longjmp () 등)
  2. 디버거에서 사용자에게 스택을 표시합니다.
  3. 프로파일 러에서 스택 샘플을 가져옵니다.
  4. 프로그램 자체에서 (예 : 스택을 표시하는 충돌 처리기에서)

이들은 미묘하게 다른 요구 사항이 있습니다. 이들 중 일부는 성능이 중요하고 일부는 그렇지 않습니다. 일부는 외부 프레임에서 레지스터를 재구성하는 기능이 필요하지만 일부는 그렇지 않습니다. 그러나 우리는 잠시 후에 모든 것을 시작할 것입니다.

여기 에서 전체 게시물을 찾을 수 있습니다 .


7

모든 사람들이 C ++의 예외 처리에 대해 이야기했습니다. 그러나 스택 풀림에 대한 또 다른 의미가 있다고 생각하며 디버깅과 관련이 있습니다. 디버거는 현재 프레임 이전의 프레임으로 이동해야 할 때마다 스택 해제를 수행해야합니다. 그러나 이것은 현재 프레임으로 돌아올 때 되감기해야하는 일종의 가상 풀기입니다. 이에 대한 예는 gdb의 up / down / bt 명령 일 수 있습니다.


5
디버거 작업은 일반적으로 스택을 구문 분석하는 "스택 워킹"이라고합니다. "스택 풀기"는 "스택 워킹"뿐만 아니라 스택에 존재하는 객체의 소멸자를 호출하는 것을 의미합니다.
Adisak

@Adisak 나는 그것이 "스택 워킹"이라고도 몰랐다. 나는 항상 모든 디버거 기사와 gdb 코드 내부에서 "스택 풀기"를 보았습니다. 모든 기능에 대한 스택 정보를 엿보기 만하는 것이 아니라 프레임 정보를 해제하는 것 (드워프의 CFI 참조)과 관련하여 "스택 풀기"가 더 적절하다고 느꼈습니다.
bbv

"스택 워킹"은 Windows에서 더 유명해 졌다고 생각합니다. 또한 드워프 표준의 문서 자체와는 별개로 code.google.com/p/google-breakpad/wiki/StackWalking 예제를 찾았 습니다. 동의하지만, 그것은 사실상 풀고 있습니다. 또한, 질문은 "스택 풀기"가 제안 할 수있는 모든 가능한 의미를 요구하는 것 같습니다.
bbv

7

기사 에서 아래에 주어진 다이어그램 인 IMO 는 다음 명령의 경로에서 스택 해제가 미치는 영향을 아름답게 설명합니다 (예외가 발생하면 실행되지 않음).

여기에 이미지 설명을 입력하십시오

그림에서 :

  • 하나는 정상적인 호출 실행입니다 (예외는 발생하지 않음).
  • 예외가 발생했을 때 맨 아래.

두 번째 경우 예외가 발생하면 함수 호출 스택에서 예외 핸들러를 선형으로 검색합니다. 검색은 예외 처리기 (예 : main()둘러싸는 try-catch블록)로 함수에서 끝나지만 함수 호출 스택에서 모든 항목을 제거 하기 전에는 끝나지 않습니다 .


다이어그램은 좋지만 설명은 약간 혼동됩니다. ... try-catch 블록을 포함하지만 함수 호출 스택에서 모든 항목을 제거하기 전에는 안됩니다 ...
Atul

3

C ++ 런타임은 throw와 catch 사이에 생성 된 모든 자동 변수를 파괴합니다. 아래의 간단한 예에서 f1 () throws 및 main () catch는 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가 파괴됩니다.

이것이 도움이되기를 바랍니다. 행복한 코딩!


2

예외가 발생하고 제어가 try 블록에서 핸들러로 전달되면 C ++ 런타임은 try 블록의 시작 이후 구성된 모든 자동 객체에 대해 소멸자를 호출합니다. 이 과정을 스택 해제라고합니다. 자동 객체는 구성 순서와 반대로 파괴됩니다. (자동 객체는 자동 또는 등록으로 선언되었거나 정적 또는 extern으로 선언되지 않은 로컬 객체입니다. 자동 객체 x는 프로그램이 x가 선언 된 블록을 종료 할 때마다 삭제됩니다.)

하위 객체 또는 배열 요소로 구성된 객체를 구성하는 동안 예외가 발생하면 예외가 발생하기 전에 해당 하위 객체 또는 배열 요소가 성공적으로 생성 된 경우에만 소멸자가 호출됩니다. 로컬 정적 객체의 소멸자는 객체가 성공적으로 생성 된 경우에만 호출됩니다.


이 답변을 복사 한 원본 기사에 대한 링크를 제공해야합니다. IBM Knowledge Base-Stack Unwinding
w128

0

Java 스택에서 풀기 또는 풀기 기능은 중요하지 않습니다 (가비지 수집기 사용). 많은 예외 처리 논문에서 나는이 개념 (스택 풀림)을 보았습니다. 특별히 그 작성자는 C 또는 C ++의 예외 처리를 처리합니다. 로 try catch블록을 우리는 잊지 shouln't : 지역 블록 이후의 모든 개체에서 무료로 스택 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.