Notice
Recent Posts
Recent Comments
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

SYDev

C++ Chapter 15-3 : Stack Unwinding(스택 풀기) 본문

Programming Lang/C++

C++ Chapter 15-3 : Stack Unwinding(스택 풀기)

시데브 2023. 8. 1. 15:58

예외의 전달

  • 함수 내에 예외처리를 위한 try~catch문이 존재하지 않으면, 예외처리에 대한 책임은 MyFunc을 호출한 영역으로 넘어가게 된다.
#include <iostream>
using namespace std;

void Divide(int num1, int num2)
{
    if(num2==0)
        throw num2;
    cout<<"나눗셈의 몫: "<< num1/num2 <<endl;
    cout<<"나눗셈의 나머지: "<< num1%num2 <<endl;
}

int main(void)
{
    int num1, num2;
    cout<<"두 개의 숫자 입력: ";
    cin>>num1>>num2;

    try
    {
        Divide(num1, num2);
        cout<<"나눗셈을 마쳤습니다."<<endl;
    }
    catch(int expn)
    {
        cout<<"제수는 "<<expn<<"이 될 수 없습니다."<<endl;
        cout<<"프로그램을 다시 실행하세요."<<endl;
    }
    
    return 0;
}

 예외 발생 X

두 개의 숫자 입력: 9
2
나눗셈의 몫: 4
나눗셈의 나머지: 1
나눗셈을 마쳤습니다.

 예외 발생

두 개의 숫자 입력: 7
0
제수는 0이 될 수 없습니다.
프로그램을 다시 실행하세요.

 

예외상황이 발생한 위치와 예외상황을 처리해야 하는 위치가 다른 경우

#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;

int StoI(char * str)    //문자열을 정수로 변환하는 함수
{
    int len=strlen(str);
    int num=0;

    for(int i=0; i<len; i++)
    {
        if(str[i]<'0' || str[i]>'9')
            throw str[i];
        num += (int)(pow((double)10, (len-1)-i) * (str[i]+(7-'7')));    //문자열을 정수 형태로 변환
    }
    return num;
}

int main(void)
{
    char str1[100];
    char str2[200];

    while(1)
    {
        cout<<"두 개의 숫자 입력: ";
        cin>>str1>>str2;

        try
        {
            cout<<str1<<" + "<<str2<<" = "<<StoI(str1)+StoI(str2)<<endl;
            break;
        }
        catch(char ch)
        {
            cout<<"문자 "<<ch<<"가 입력되었습니다."<<endl;
            cout<<"재입력 진행합니다."<<endl<<endl;
        }
    }
    cout<<"프로그램을 종료합니다."<<endl;

    return 0;
}
두 개의 숫자 입력: 123
3A5
123 + 3A5 = 문자 A가 입력되었습니다.
재입력 진행합니다.

두 개의 숫자 입력: 28F
211
28F + 211 = 문자 F가 입력되었습니다.
재입력 진행합니다.

두 개의 숫자 입력: 231
891
231 + 891 = 1122
프로그램을 종료합니다.

 

스택 풀기(Stack Unwinding)

  • 스택 풀기: 예외가 처리되지 않아서, 함수를 호출한 영역으로 예외 데이터가 전달되는 현상

 예제를 살펴보자.

#include <iostream>
using namespace std;

void SimpleFuncOne(void);
void SimpleFuncTwo(void);
void SimpleFuncThree(void);

int main(void)
{
    try
    {
        SimpleFuncOne();
    }
    catch(int expn)
    {
        cout<<"예외코드: "<<expn<<endl;
    }

    return 0;
}

void SimpleFuncOne(void)
{
    cout<<"SimpleFuncOne(void)"<<endl;
    SimpleFuncTwo();
}

void SimpleFuncTwo(void)
{
    cout<<"SimpleFuncTwo(void)"<<endl;
    SimpleFuncThree();
}

void SimpleFuncThree(void)
{
    cout<<"SimpleFuncThree(void)"<<endl;
    throw -1;
}
SimpleFuncOne(void)
SimpleFuncTwo(void)
SimpleFuncThree(void)
예외코드: -1
  • 함수의 호출 순서 main -> SimpleFuncOne -> SimpleFuncTwo -> SimpleFuncThree (스택이 쌓이는 방향)
  • 예외의 전달 순서 SimpleFuncThree -> SimpleFuncTwo -> SimpleFuncOne -> main (스택이 해제되는 순서)

  '예외' 데이터를 전달하면서 해당 데이터를 전달한 함수는 종료되기 때문에, '예외' 데이터를 전달한 함수의 스택이 반환된다. 따라서, '예외' 데이터의 전달을 가리켜 'Stack Unwinding(스택 풀기)' 또는 스택의 반환이라고 한다.

 

 마지막으로 main 함수에 '예외' 데이터에 대한 처리문이 없다면 함수 종료를 위한 terminate 함수가 호출되면서 프로그램이 종료된다.

 

자료형이 일치하지 않는 경우의 예외 데이터

#include <iostream>
using namespace std;

void SimpleFuncOne(void);
void SimpleFuncTwo(void);
void SimpleFuncThree(void);

int main(void)
{
    try
    {
        SimpleFuncOne();
    }
    catch(char expn)	//throw하는 데이터인 -1과 자료형이 일치하지 않음
    {
        cout<<"예외코드: "<<expn<<endl;
    }

    return 0;
}

void SimpleFuncOne(void)
{
    cout<<"SimpleFuncOne(void)"<<endl;
    SimpleFuncTwo();
}

void SimpleFuncTwo(void)
{
    cout<<"SimpleFuncTwo(void)"<<endl;
    SimpleFuncThree();
}

void SimpleFuncThree(void)
{
    cout<<"SimpleFuncThree(void)"<<endl;
    throw -1;
}
SimpleFuncOne(void)
SimpleFuncTwo(void)
SimpleFuncThree(void)
terminate called after throwing an instance of 'int'

 throw하는 데이터와 catch의 매개변수 자료형을 일치시키지 않으면 catch 블록으로 값이 전달되지 않으며, 위와 같이 terminate 함수가 호출되며 프로그램이 종료된다.

 

하나의 try 블록과 다수의 catch 블록

#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;

int StoI(char * str)   
{
    int len=strlen(str);
    int num=0;

    if(len!=0 && str[0]=='0')
        throw 0;

    for(int i=0; i<len; i++)
    {
        if(str[i]<'0' || str[i]>'9')
            throw str[i];
        num += (int)(pow((double)10, (len-1)-i) * (str[i]+(7-'7')));   
    }
    return num;
}

int main(void)
{
    char str1[100];
    char str2[200];

    while(1)
    {
        cout<<"두 개의 숫자 입력: ";
        cin>>str1>>str2;

        try
        {
            cout<<str1<<" + "<<str2<<" = "<<StoI(str1)+StoI(str2)<<endl;
            break;
        }
        catch(char ch)
        {
            cout<<"문자 "<<ch<<"가 입력되었습니다."<<endl;
            cout<<"재입력 진행합니다."<<endl<<endl;
        }
        catch(int expn)
        {
            if(expn==0)
                cout<<"0으로 시작하는 숫자는 입력 불가."<<endl;
            else
                cout<<"비정상적으로 입력이 이루어졌습니다."<<endl;

            cout<<"재입력 진행합니다."<<endl<<endl;
        }
    }
    cout<<"프로그램을 종료합니다."<<endl;

    return 0;
}
두 개의 숫자 입력: 12A
519
12A + 519 = 문자 A가 입력되었습니다.
재입력 진행합니다.

두 개의 숫자 입력: 082
910
082 + 910 = 0으로 시작하는 숫자는 입력 불가.
재입력 진행합니다.

두 개의 숫자 입력: 123
456
123 + 456 = 579
프로그램을 종료합니다.

 

전달되는 예외의 명시

  • 함수의 호출을 위해서 함수의 이름, 매개변수 선언, 반환형 정보 말고도 함수 내에서 전달될 수 있는 예외의 종류(예외 데이터의 자료형)와 그 상황도 알아야 한다.
int ThrowFunc(int num) throw (int, char)
{
    . . . .
}

 위의 원형 선언을 통해서 다음과 같이 호출문을 구성할 수 있다.

try
{
    . . . .
    ThrowFunc(20);
    . . . . 
}
catch(int expn) { . . . . }
catch(char expn) { . . . . }

 위 경우에는 int, char형과 다른 자료형의 예외 데이터가 전달되면 terminate 함수가 호출되어 프로그램이 종료된다.

 

 마지막으로 어떤 예외도 전달되지 않은 경우는 다음과 같은 형태를 보인다.

int ThrowFunc(int num) throw ( )
{
    . . . .
}

 


참고자료

 

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

이전 포스팅에서 함수 내에서 throw를 던진 경우에 그 함수를 호출한 영역으로 돌아가서 예외처리를 하였다. 이렇듯, 예외처리에 대한 책임은 throw가 발생한 함수를 호출한 영역으로 넘어가게 된

musket-ade.tistory.com