Quick Flow
여러 자원을 순서대로 얻는 함수는 실패와 성공을 하나의 cleanup 경로로 모읍니다. 포인터와 핸들을 NULL로 시작하면 얻지 못한 자원도 같은 정리 코드에서 안전하게 처리할 수 있습니다.
#include <stdio.h>
#include <stdlib.h>
int read_exact(const char *path, size_t size) {
FILE *fp = NULL;
unsigned char *buffer = NULL;
int ok = 0;
if (path == NULL) return 0;
fp = fopen(path, "rb");
if (fp == NULL) goto cleanup;
if (size != 0) {
buffer = malloc(size);
if (buffer == NULL) goto cleanup;
if (fread(buffer, 1, size, fp) != size) goto cleanup;
}
ok = 1;
cleanup:
free(buffer); /* 획득의 역순으로 정리한다. */
if (fp != NULL && fclose(fp) != 0) ok = 0;
return ok;
}자원이 하나라면 직접 반환 전 해제로 충분하지만, 여러 자원을 순서대로 얻으면 실패 지점을 하나의 cleanup label로 모읍니다. label은 정리 흐름에만 쓰고, 성공 여부와 fclose 실패는 분리해 기록합니다.
문법
여러 자원을 단계적으로 획득하고, 중간 실패 시 이미 잡은 자원을 역순으로 정리해야 할 때 goto cleanup 패턴이 가장 단순해집니다.
int process(void) {
int *buf1 = NULL;
int *buf2 = NULL;
FILE *fp = NULL;
int ret = -1;
buf1 = malloc(100 * sizeof(int));
if (buf1 == NULL) goto cleanup;
buf2 = malloc(200 * sizeof(int));
if (buf2 == NULL) goto cleanup;
fp = fopen("data.bin", "rb");
if (fp == NULL) goto cleanup;
ret = 0;
cleanup:
if (fp) fclose(fp);
free(buf2);
free(buf1);
return ret;
}이 패턴의 목적은 흐름을 복잡하게 만드는 것이 아니라, 자원 정리 책임을 한곳에 모으는 것입니다.
성공 경로에서도 cleanup으로 흘려보내면 해제 순서가 하나로 고정됩니다. 쓰기 파일을 닫는 fclose는 buffered write 오류를 반환할 수 있으므로, 결과가 중요한 함수라면 cleanup 직전의 작업 성공과 close 실패를 구분해 반환 정책을 정합니다.
빠른 점검
- 앞으로만 점프합니다.
- 함수 끝의 cleanup 레이블로만 이동합니다.
- 자원은 획득 역순으로 해제합니다.
- 포인터와 핸들은 NULL로 초기화해 둡니다.
- 자원이 하나뿐이면 굳이
goto까지 쓰지 않아도 됩니다.
주의할 점
goto는 정리 레이블로 좁혀 쓰면 유용하지만, 임의 위치 점프나 뒤로 점프까지 허용하기 시작하면 코드 가독성이 급격히 떨어집니다. C에서 goto를 쓰더라도 "자원 정리 전용"으로 제한하는 편이 맞습니다.
참고 링크
2 sources