Phase 3 · 자료구조 및 실무 프로그래밍

전처리기와 매크로 — 코드 준비와 모듈화

하나의 파일(main.c)에 모든 코드를 때려 넣던 방식에서 벗어납니다. 컴파일 직전 코드를 치환하는 전처리기(#)를 활용해 상수/함수 매크로를 만들고, 소스 파일(.c)과 헤더 파일(.h)을 분리하여 거대한 실무 프로젝트를 관리하는 방법을 배웁니다.

예상 학습 시간 : 120분
난이도 : ★★★☆☆ 중급
사전 지식 : C언어 기본 문법
다음 단계 : 자료구조 기초 (Step 15)
01

학습 목표

1
전처리기(Preprocessor)가 컴파일 전 코드를 텍스트 단위로 치환하는 원리를 이해한다.
2
#define을 사용하여 유지보수가 쉬운 매크로 상수와 매크로 함수를 선언한다.
3
#ifdef, #ifndef를 활용하여 운영체제나 디버그 상태에 따른 조건부 컴파일을 수행한다.
4
프로젝트를 main.c 하나에서 기능별 .c.h 파일 여러 개로 모듈화(분리)한다.
5
헤더 파일 중복 포함을 막는 Include Guard(#ifndef ~ #define)를 완벽히 이해한다.
02

전처리기 (Preprocessor) 란?

C언어 코드는 작성하자마자 바로 기계어로 번역(컴파일)되지 않습니다. 컴파일러가 작동하기 직전에 '전처리기'라는 녀석이 코드를 한 번 훑으면서 #으로 시작하는 명령들을 찾아 텍스트를 치환하거나 코드를 삭제/포함 시키는 사전 작업(전처리)을 합니다.

⚙️ Compile Flow
소스 코드 (.c)
     
[ 전처리기 작동 ] <-- #include 긁어오기, #define 치환하기 등 처리
     
컴파일러 (.obj)
     
링커 (.exe / 실행파일)
03

#define 매크로 (상수 & 함수)

코드 내에서 반복해서 쓰이는 숫자나 공식을 #define으로 정의하면 전처리기가 컴파일 전 해당 텍스트를 통째로 치환해 줍니다.

⚙️ C
#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이 나와버립니다. 매크로는 단순 '텍스트 복사 붙여넣기'라는 것을 잊지 마세요!

04

조건부 컴파일 (#ifdef)

특정 매크로가 정의되어 있을 때만 특정 코드를 컴파일 대상에 포함시킬 수 있습니다. 이는 개발용(Debug) 모드와 배포용(Release) 모드를 분리하거나 플랫폼(Windows/Linux) 별로 다른 코드를 짤 때 극강의 효율을 발휘합니다.

🐛
Practice
개발/배포 모드 스위칭
⚙️ C
#define DEBUG // 주석 처리하면 아래 DEBUG용 코드가 컴파일에서 아예 제외됨!

int main() {
#ifdef DEBUG
    printf("[로그] 회원 데이터 로드 완료!\n");
#endif

    printf("일반 유저 화면 출력\n");
    return 0;
}
05

헤더 파일 분리와 모듈화

현업에서는 절대 main.c 파일 하나에 수천 줄의 코드를 작성하지 않습니다. 계산 기능은 calc.c, 회원 기능은 member.c 로 분리하고, 각 기능의 '사용 설명서(함수 선언)'를 .h 파일에 담아 이어 붙이는 모듈화를 적용합니다.

calculator.h
함수 선언부
int add(int, int);
calculator.c
실제 함수 몸체
return a + b;
main.c
#include "calculator.h"
add(10, 20);
06

Include Guard: 무한루프(중복 포함) 방어

헤더 파일 안에 또 다른 헤더 파일을 Include 하는 일이 많아지면, A.h를 두 번 포함하게 되어 '구조체 재정의 에러'가 뜹니다. 이를 방지하기 위해 헤더 파일 내부에 무조건 작성해야 하는 방어막이 Include Guard입니다.

⚙️ member.h
#ifndef MEMBER_H  // MEMBER_H 가 정의되지 않았다면 (최초 방문 시 참)
#define MEMBER_H  // 지금 즉시 정의해라! (두 번째 방문 시엔 위에서 걸러짐)

typedef struct {
    int id;
    char name[50];
} Member;

void print_member(Member m);

#endif            // 방어막 종료
07

STEP 14 최종 프로젝트

🚀 Mission

모듈형 회원 관리 프로그램 분해/조립

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 하여 메뉴 스위칭만 담당
08

STEP 14 완료 체크리스트

전처리기가 코드를 컴파일하기 전 단순 "텍스트 치환"을 수행한다는 사실을 이해한다.
#define 을 통해 상수 및 함수형 매크로를 작성할 때 괄호() 사용의 중요성을 파악한다.
#ifdef, #ifndef, #endif 의 논리를 이해하고 디버그 전용 코드를 심을 수 있다.
함수의 선언은 헤더(.h)에, 함수의 본체는 소스(.c)에 나누어 작성하는 모듈화 패턴을 몸에 익힌다.
내가 생성하는 모든 사용자 정의 헤더 파일에 Include Guard 방어막 패턴을 기계적으로 작성할 수 있다.
실무 프로그래머의 자격 획득!

이제 여러분은 1만 줄이 넘어가는 방대한 코드를 만나도 당황하지 않고, 폴더와 모듈(헤더/소스 분리)을 넘나들며 시스템을 설계하는 역량을 갖추었습니다. 프로그래밍 기초 문법과 모듈화의 끝을 맺으며, 다음 단계부터는 프로그래밍의 영원한 숙제, 자료구조(Data Structure) 기초를 향해 나아갑니다.

이전 : Step 13 Step 15 : 자료구조 기초