전처리기와 매크로 — 코드 준비와 모듈화
하나의 파일(main.c)에 모든 코드를 때려 넣던 방식에서 벗어납니다. 컴파일 직전 코드를 치환하는 전처리기(#)를 활용해 상수/함수 매크로를 만들고, 소스 파일(.c)과 헤더 파일(.h)을 분리하여 거대한 실무 프로젝트를 관리하는 방법을 배웁니다.
학습 목표
#define을 사용하여 유지보수가 쉬운 매크로 상수와 매크로 함수를 선언한다.#ifdef, #ifndef를 활용하여 운영체제나 디버그 상태에 따른 조건부 컴파일을 수행한다.main.c 하나에서 기능별 .c 및 .h 파일 여러 개로 모듈화(분리)한다.#ifndef ~ #define)를 완벽히 이해한다.전처리기 (Preprocessor) 란?
C언어 코드는 작성하자마자 바로 기계어로 번역(컴파일)되지 않습니다. 컴파일러가 작동하기 직전에 '전처리기'라는 녀석이 코드를 한 번 훑으면서 #으로 시작하는 명령들을 찾아 텍스트를 치환하거나 코드를 삭제/포함 시키는 사전 작업(전처리)을 합니다.
소스 코드 (.c) ↓ [ 전처리기 작동 ] <-- #include 긁어오기, #define 치환하기 등 처리 ↓ 컴파일러 (.obj) ↓ 링커 (.exe / 실행파일)
#define 매크로 (상수 & 함수)
코드 내에서 반복해서 쓰이는 숫자나 공식을 #define으로 정의하면 전처리기가 컴파일 전 해당 텍스트를 통째로 치환해 줍니다.
#define MAX_STUDENT 100 // 매크로 상수 (대문자 관례, 세미콜론 없음!) #define SQUARE(x) ((x)*(x)) // 매크로 함수 (반드시 괄호로 도배해야 함!) int main() { // 전처리기가 작동하면 아래 코드는 int students[100]; 으로 변합니다. int students[MAX_STUDENT]; // SQUARE(2+3) -> ((2+3)*(2+3)) 로 치환됩니다. printf("결과: %d\n", SQUARE(2+3)); return 0; }
만약 #define SQUARE(x) x * x 라고 정의해버리면, SQUARE(2+3)은 전처리 후 2 + 3 * 2 + 3 이 되어 전혀 다른 결과값인 11이 나와버립니다. 매크로는 단순 '텍스트 복사 붙여넣기'라는 것을 잊지 마세요!
조건부 컴파일 (#ifdef)
특정 매크로가 정의되어 있을 때만 특정 코드를 컴파일 대상에 포함시킬 수 있습니다. 이는 개발용(Debug) 모드와 배포용(Release) 모드를 분리하거나 플랫폼(Windows/Linux) 별로 다른 코드를 짤 때 극강의 효율을 발휘합니다.
#define DEBUG // 주석 처리하면 아래 DEBUG용 코드가 컴파일에서 아예 제외됨! int main() { #ifdef DEBUG printf("[로그] 회원 데이터 로드 완료!\n"); #endif printf("일반 유저 화면 출력\n"); return 0; }
헤더 파일 분리와 모듈화
현업에서는 절대 main.c 파일 하나에 수천 줄의 코드를 작성하지 않습니다. 계산 기능은 calc.c, 회원 기능은 member.c 로 분리하고, 각 기능의 '사용 설명서(함수 선언)'를 .h 파일에 담아 이어 붙이는 모듈화를 적용합니다.
함수 선언부
int add(int, int);
실제 함수 몸체
return a + b;
#include "calculator.h"
add(10, 20);
Include Guard: 무한루프(중복 포함) 방어
헤더 파일 안에 또 다른 헤더 파일을 Include 하는 일이 많아지면, A.h를 두 번 포함하게 되어 '구조체 재정의 에러'가 뜹니다. 이를 방지하기 위해 헤더 파일 내부에 무조건 작성해야 하는 방어막이 Include Guard입니다.
#ifndef MEMBER_H // MEMBER_H 가 정의되지 않았다면 (최초 방문 시 참) #define MEMBER_H // 지금 즉시 정의해라! (두 번째 방문 시엔 위에서 걸러짐) typedef struct { int id; char name[50]; } Member; void print_member(Member m); #endif // 방어막 종료
STEP 14 최종 프로젝트
모듈형 회원 관리 프로그램 분해/조립
STEP 13에서 짰던 1개의 거대한 소스코드를 아래와 같이 5개의 모듈 파일로 완전히 쪼갠 뒤 다중 컴파일(gcc main.c member.c file.c -o app)을 통해 하나의 앱으로 구동시켜 보세요.
- [1]
config.h: MAX_CAPACITY 등 매크로 상수 저장 - [2]
member.h / .c: 동적 배열 할당(malloc/realloc) 및 데이터 관리 함수들 - [3]
file.h / .c: fread, fwrite 등 File I/O 함수들 - [4]
main.c: 위의 헤더들을 모두 Include 하여 메뉴 스위칭만 담당
STEP 14 완료 체크리스트
이제 여러분은 1만 줄이 넘어가는 방대한 코드를 만나도 당황하지 않고, 폴더와 모듈(헤더/소스 분리)을 넘나들며 시스템을 설계하는 역량을 갖추었습니다. 프로그래밍 기초 문법과 모듈화의 끝을 맺으며, 다음 단계부터는 프로그래밍의 영원한 숙제, 자료구조(Data Structure) 기초를 향해 나아갑니다.