Search
Duplicate

13장. 예외 처리와 C언어와의 링크 지정

생성일
2022/06/06 14:37
태그
C++

13장. 예외 처리와 C언어와의 링크 지정

예제 13-1) 예외 상황에 대한 대처가 없는 프로그램 사례 → 문제
#include <iostream> using namespace std; int getExp(int base, int exp) { // base의 exp 지수승을 계산하여 리턴 int value = 1; for (int n = 0; n < exp; n++) value = value * base; // base를 exp번 곱하여 지수 값 계산 return value; } int main() { int v = getExp(2, 3); cout << "2의 3승은 " << v << "입니다." << endl; int e = getExp(2, -3); cout << "2의 -3승은 " << e << "입니다." << endl; // -> 오류 발생! 1/8이 나와야하는데 -1이 출력된다 }
C++
복사
예제 13-2) if문리턴 값을 이용한 오류 처리
getExp()의 리턴 값이 오류 상태와 계산값을 함께 표시하는 예민한 코드
#include <iostream> using namespace std; int getExp(int base, int exp) { // base의 exp 지수승을 계산하여 리턴 if (base <= 0 || exp <= 0) { return -1; // 오류 리턴 } int value = 1; for (int n = 0; n < exp; n++) value = value * base; // base를 exp번 곱하여 지수 값 계산 return value; } int main() { int v = 0; v = getExp(2, 3); if (v != -1) cout << "2의 3승은 " << v << "입니다." << endl; else cout << "오류. 2의 3승은 " << "계산할 수 없습니다." << endl; int e = 0; e = getExp(2, -3); // 2의 -3승? getExp()는 false 리턴 if (e != -1) cout << "2의 -3승은 " << e << "입니다." << endl; else cout << "오류. 2의 -3승은" << "계산할 수 없습니다." << endl; }
C++
복사
예제 13-3) 리턴 값과 참조 매개 변수를 이용한 오류 처리
getExp()의 리턴 값의 단일화 - 오류 상태만 표시
참조 매개변수를 통해 계산 값을 전달하는 정리된 코드
#include <iostream> using namespace std; int getExp(int base, int exp, int &ret) { // base ** exp 값을 계산하여 ret에 저장 if (base <= 0 || exp <= 0) { return false; } int value = 1; for (int n = 0; n < exp; n++) value = value * base; // base를 exp번 곱하여 지수 값 계산 ret = value; return true; } int main() { int v = 0; if (getExp(2, 3, v)) // v = 2**3 = 8, getExp()는 true 리턴 cout << "2의 3승은 " << v << "입니다." << endl; else cout << "오류. 2의 3승은 " << "계산할 수 없습니다." << endl; int e = 0; if (getExp(2, -3, e)) // 2**-3 ? -> getExp()는 false 리턴 cout << "2의 -3승은 " << e << "입니다." << endl; else cout << "오류. 2의 -3승은 " << "계산할 수 없습니다." << endl; }
C++
복사

예외

예외

실행 중, 프로그램 오동작이나 결과에 영향을 미치는 예상치 못한 상황 발생

예외 처리기

예외 발생을 탐지하고 예외를 처리하는 코드
잘못된 결과, 비정상적인 실행, 시스템에 의한 강제 종료를 막음

예외 처리 수준

운영체제 수준 예외 처리
운영체제가 예외의 발생을 탐지하여, 응용프로그램에게 알려주어 예외에 대처하게 함
OS마다 서로 다르므로, OS나 컴파일러 별로 예외 처리 라이브러리로 작성
응용프로그램 수준 예외 처리
사용자의 잘못된 입력이나
없는 파일을 여는 등, 응용프로그램 수준에서 발생하는 예외를 자체적으로 탐지하고 처리하는 방법

C++ 예외 처리

C++ 표준의 예외 처리
응용프로그램 수준 예외 처리

C++ 예외 처리 기본 형식

try-throw-catch

try {} 블록

예외가 발생할 가능성이 있는 코드를 묶음

throw문

발견된 예외를 처리하기 위해, 예외 발생을 알리는 문장
try{} 블록 내에서 이루어져야 함

catch() {} 블록

throw에 의해 발생한 예외를 처리하는 코드
try { .... 예외 발견한다면 { throw XXX; // 예외 발생 알림. XXX는 예외 값 } 예외 발견한다면 { throw YYY; } } catch(처리할 예외 파라미터 선언) { // catch {} 블록 예외 처리문 } catch(처리할 예외 파라미터 선언) { // catch {} 블록 예외 처리문 }
C++
복사

throw와 catch

throw 3; ... catch(int x) { // throw 3; 이 실행되면 catch()문 실행, x에 3이 전달 ... }
C++
복사
예)
try { throw 3.5; } catch(double d) { ... } /////////////////// try { throw "음수 불가능"; } catch(const char* s) { // const char* 타입의 예외 처리, 얘외 값은 "음수 불가능"이 s에 전달됨 cout << s; }
C++
복사
예제 13-4) 0으로 나누는 예외 처리
#include <iostream> using namespace std; int main() { int n, sum, average; while(true) { cout << "합을 입력하세요>> "; cin >> sum; cout << "인원 수를 입력하세요>> "; cin >> n; try { if (n <= 0) throw n; else average = sum / n; } catch(int x) { cout << "예외 발생!! " << x << "으로 나눌 수 없음" << endl; average = 0; cout << endl; continue; } cout << "average = " << average << endl << endl; } }
C++
복사

하나의 try {} 블록에 다수의 catch() {} 블록 연결

try { ... throw "음수 불가능"; ... throw 3; ... } // 문자열 타입 예외 처리, "음수 불가능"이 s에 전달 catch (const char* s) { ... } // int 타입 예외 처리, 3이 x에 전달됨 catch (int x) { ... }
C++
복사

함수를 포함하는 try {} 블록

try { int n = multiply(2, -3); } catch (const char* negative) { }
C++
복사
예제 13-5) 지수 승 계산을 예외 처리 코드로 재작성 (완결판)
#include <iostream> using namespace std; int getExp(int base, int exp) { if (base <= 0 || exp <= 0) { throw "음수 사용 불가"; } int value = 1; for (int n = 0; n < exp; n++) { value = value * base; return value; // 계산 결과 리턴 } } int main() { int v = 0; try { v = getExp(2, 3); // v = 2의 3승 = 8 cout << "2의 3승은 " << v << "입니다." << endl; v = getExp(2, -3); // 2의 -3승은? cout << "2의 -3승은 " << v << "입니다." << endl; } catch(const char *s) { cout << s <<endl; } }
C++
복사
예제 13-6) 문자열을 정수로 변환하기
#include <iostream> #include <cstring> using namespace std; // 문자열 -> 정수 // 정수로 변환하기 어려운 문자열의 경우, char* 타입 얘외 발생 int stringToInt(const char x[]) { int sum = 0; int len = strlen(x); for (int i = 0; i < len; i++) { if (x[i] >= '0' && x[i] <= '9') sum = sum*10 + x[i] - '0'; else throw x; // char* 타입의 예외 발생 } return sum; } int main() { int n; try { n = stringToInt("123"); cout << "\"123\" 은 정수 " << n << "로 변환됨" << endl; n = stringToInt("1A3"); // 문자열을 정수로 변환 cout << "\"1A3\" 은 정수 " << n << "로 변환됨" << endl; } catch(const char* s) { cout << s << "처리에서 예외 발생!!" << endl; return 0; } }
C++
복사

예외를 발생시키는 함수의 선언

함수 원형에 연이어 throw(예외 타입, 예외 타입, …) 선언
int max(int x, int y) throw(int) { if (x < 0) throw x; else if (y < 0) throw y; else if (x > y) return x; else return y; } double valueAt(double *p, int index) throw(int, char*) { if (index < 0) throw "index out of bounds exception"; // char* 타입 예외 발생 else if (p == NULL) throw 0; // int 타입 예외 발생 else return p[index]; }
C++
복사
→ 장점
프로그램의 작동을 명확히 함
프로그램의 가동성 높임
예제 13-7) 예외 처리를 가진 스택 클래스 만들기
#include <iostream> class MyStack { int data[100]; int tos; public: MyStack() { tos = -1; } void push(int n) throw(char*); int pop() throw(char*); } void MyStack::push(int n) { if (tos == 99) throw "Stack Full"; tos++; data[tos] = n; } int MyStack::pop() { if (tos == -1) throw "Stack Empty"; int rData = data[tos--]; return rData; } int main() { MyStack intStack; try { intStack.push(100); intStack.push(200); cout << intStack.pop() << endl; cout << intStack.pop() << endl; cout << intStack.pop() << endl; // "Stack Empty" 예외 발생 } catch(const char* s) { cout << "예외 발생 : " << s << endl; } }
C++
복사

다중 try {} 블록

try {} 블록 내, try {} 블록의 중첩 가능

throw 사용 시 주의사항

throw문의 위치
항상 try {} 블록 안에서 실행
시스템이 abort() 호출, 강제 종료
예외를 처리할 catch()가 없으면 → 프로그램 강제 종료
catch() { } 블록 내에도, try { } catch() { } 블록 선언 가능

예외 클래스 만들기

예외 값의 종류

기본 타입의 예외 값
정수, 실수, 문자열 등 비교적 간단한 예외 정보 전달
객체 예외 값
예외 값으로 객체를 던질 수 있디
예외 값으로 사용할 예외 클래스 작성 필요

예외 클래스

사용자는 자신 만의 예외 정보를 포함하는 클래스 작성
throw로 객체를 던짐
객체가 복사되어 예외 파라미터에 전달
예제 13-8) 예외 클래스 만들기
#include <iostream> #include <string> using namespace std; class MyException { // 사용자가 만드는 기본 예외 클래스 선언 int lineNo; string func, msg; public: MyException(int n, string f, string m) { lineNo = n; func = f; msg = m; } void print() { cout << func << " : " << lineNo << " ," << msg << endl; } }; class DivideByZeroException : public MyException { // 0으로 나누는 예외 클래스 선언 public: DivideByZeroException (int lineNo, string func, string msg) : MyException(lineNo, func, msg) {} }; class InvalidInputException : public MyException { // 잘못된 입력 예외 클래스 선언 public: InvalidInputException(int lineNo, string func, string msg) : MyException(lineNo, func, msg) {} }; int main() { int x, y; try { cout << "나눗셈을 합니다. 두개의 양의 정수를 입력하세요>> "; cin >> x >> y; if (x < 0 || y < 0) throw InvalidInputException(32, "main()", "음수 입력 예외 발생"); if (y == 0) throw DivideByZeroException(34, "main()", "0으로 나누는 예외 발생"); cout << (double)x / (double)y; } catch(DivideByZeroException &e) { e.print(); } catch(InvalidInputException &e) { e.print(); } }
C++
복사

C++ 코드에서 C 코드의 링킹

이름 규칙 (naming mangling)

컴파일 후 목적 코드에 이름 붙이는 규칙
변수, 함수, 클래스 등의 이름

C언어의 이름 규칙

이름 앞에 밑줄표시문자(_)를 붙인다
int f (int x, int y) → _f
int main() → _main

C++의 이름 규칙

함수의 매개 변수 타입과 개수, 리턴 타입에 따라 복잡한 이름

C++에서 C 함수 호출 시, 링크 오류 발생

extern “c”

C 컴파일러로 컴파일할 것을 지시
C이름 규칙으로 목적 코드를 생성할 것을 지시

사용법

함수 하나만 선언
extern "C" int f(int x, int y);
C++
복사
여러 함수들 선언
extern "C" { int f(int x, int y); void g(); char s(int []); }
C++
복사
헤더 파일 통째로 선언
extern "C" { #include "mycunction.h" }
C++
복사