본문 바로가기

C++

[C++] 스택 풀기(Stack unwinding)

함수가 함수를 호출하면 매개 변수, 자동 변수 등 추가 정보가 모두 스택 메모리에 쌓인다. 

 

TestFunc3() -> TestFunc2() -> TestFunc1()을 순서대로 호출하였다고 가정하자. TestFunc1()에서 예외가 발생하여 TestFunc3() 초기 함수가 불리기 전으로 돌아가야 한다면 스택 메모리에서 쌓여 올려진 모든 메모리를 반환하도록 해야 한다.

 

예외 발생이 없는 경우 TestFunc1()이 실행되고 TestFunc1() 함수가 끝나면 블록을 벗어나면서 TestFunc1() 함수가 자동으로 스택 메모리에서 사라진다. 그리고 순차적으로 TestFunc2(), TestFunc3() 함수가 스택 메모리에서 사라질 것이다.

 

TestFunc3() start
TestFunc2() start
TestFunc1() start
TestFunc1() end
TestFunc2() end
TestFunc3() end

 

 

그러나, TestFunc1() 실행 도중 예외가 발생하면 TestFunc2(), TestFunc3() 함수가 모두 반환되도록 코드를 작성해야 한다. 그러려면 관련 상황에 맞는 코드들이 각 함수에 들어있어야 하지만 구조화된 예외 처리를 이용한다면 한 번에 해결할 수 있다. 이것이 스택 풀기(Stack unwinding)이다.

 

#include <iostream>
using namespace std;

void TestFunc1()
{
	cout << "TestFunc1() start" << endl;
	throw 0;
	cout << "TestFunc1() end" << endl;
}

void TestFunc2()
{
	cout << "TestFunc2() start" << endl;
	TestFunc1();
	cout << "TestFunc2() end" << endl;
}

void TestFunc3()
{
	cout << "TestFunc3() start" << endl;
	TestFunc2();
	cout << "TestFunc3() end" << endl;
}

int main()
{
	try {
		TestFunc3();
	}

	catch (...)
	{
		cout << "Exception catch" << endl;
	}
	
	return 0;
}

 

결과값:

TestFunc3() start
TestFunc2() start
TestFunc1() start
Exception catch

 

비주얼 스튜디오(Visual Studio)로 호출 스택으로 확인할 수 있다.

TesFunc1() 함수 시작점과 throw코드에 break point를 걸고 디버그 모드로 들어가면

 

TestFunc1() 함수 실행초기

 

정상적으로 호출 스택이 쌓여있는 모습을 확인할 수 있다.

throw코드가 실행되고 throw 코드 실행 직후 호출 스택은 다음과 같다.

 

throw 코드 실행 직후

 

호출 스택에 3개의 함수가 정상적으로 해제되었음을 확인할 수 있다.

 

예외 처리를 잘해줄수록 프로그램의 완성도는 높아진다. 하지만 지나친 예외 처리를 위한 if문이 너무 많아지면 구조적으로 복잡해지니 예외 클래스 설계나 스택 풀기 등으로 문제를 해결하면 코드가 간결 해질 수 있다.