1. Goto를 사용한 에러핸들링
프로그래밍을 하다보면 발생 가능한 에러들에 대해서 신경을 써야 한다. 버그는 신중한 개발자가 좋은 코드를 짠다면 최소화 할 수 있는 반면, 에러는 좋은 코드에서든 나쁜 코드에서든 발생한다. 결국 우리는 에러가 발생할 수 있다는 사실을 받아들이고, 이를 어떻게 처리할 지 생각해야 한다. 다음은 흔히들 마주칠 수 있는 에러들의 예시이다.
- 파일 열기
- 파일 입출력
- 소켓 통신
- 메모리 할당
C언어 이후에 개발된 언어들은 대부분 Try-Catch 혹은 이와 유사한 구문을 지원한다. Try-Catch 문에 익숙하지 않다면 아래 Java 프로그램의 예시를 살펴보자.
다음은 파일을 열고, 내용을 한줄씩 출력하는 Java 프로그램이다.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Main {
public static void main(String[] args){
try{
File file = new File("helloworld.txt");
FileReader filereader = new FileReader(file);
BufferedReader bufReader = new BufferedReader(filereader);
String line = "";
while((line = bufReader.readLine()) != null){
System.out.println(line);
}
bufReader.close();
}catch (FileNotFoundException e) {
// 파일 Open 실패 시 발생
System.out.println("File Not Found Error");
}catch(IOException e){
// IO 실패 시 발생
System.out.println("IO Error");
}
}
}
Java에서는 Try/Catch를 사용해서 발생하는 에러를 처리한다. 위의 예제에서는 에러 발생시에 간단히 로그를 출력했지만, 없는 파일을 생성하거나, 이미 열려있는 리소스를 정리하는 등의 작업들을 수행할 수 있다.
C에는 Try/Catch가 없다. 일반적으로 C에서는 Try/Catch 대신 if 문으로 에러 발생 여부를 체크한다. 다음은 파일을 열고, 내용을 한 줄씩 출력하는 프로그램이다.
#include <stdio.h>
int main() {
char line[1024];
FILE *fp = fopen("helloworld.txt", "r");
if (fp == NULL) {
printf("File Not Found Error\n");
return 1;
}
char *ret = NULL;
do {
ret = fgets(line, sizeof(line), fp);
if (ferror(fp)) {
printf("IO Error\n");
fclose(fp); /* fclose로 열린 파일을 close 해야 한다. */
return 1;
}
printf("%s\n", line);
}while(ret);
fclose(fp);
return 0;
}
Java 프로그램과 동일하게 동작하는 코드이다. 위의 예에서 보이듯이 C에서도 if 문을 활용해서 예외처리가 가능하다. 하지만 if 문을 사용하면 fclose가 두 군데에서 호출되는 것처럼 리소스 해제 등의 정리 작업이 별도로 이뤄져야 한다. 지금은 열려 있는 파일을 닫기만 하면 되지만, 복잡한 장치, 코드를 정리하게 된다면 중복되는 코드는 더 많고 복잡해 질 것이다. 이 때 사용 가능한 방법이 Goto 문이다. Goto문을 사용하면 위의 코드가 아래와 같이 정리된다.
#include <stdio.h>
int main() {
char line[1024];
FILE *fp = fopen("helloworld.txt", "r");
if (fp == NULL) {
printf("File Not Found Error\n");
return 1;
}
char *ret = NULL;
do {
ret = fgets(line, sizeof(line), fp);
if (ferror(fp)) {
printf("IO Error\n");
goto out;
}
printf("%s\n", line);
}while(ret);
out:
fclose(fp); /* 리소스 정리가 한 군데에서 이뤄진다. */
return 0;
}
리소스 정리가 한 군데에서 이뤄지기 때문에 코드의 중복과 휴먼 에러에 의한 정리 누락을 예방할 수 있다. 위의 예시는 아주 간단한 코드이고 실전에서는 goto 문을 중첩으로 쌓아서 단계별 에러 핸들링을 할 수 있다. goto 문의 중첩을 활용한 에러핸들링은 다음 포스트에서 살펴보자.