Journey to Security/C언어

C언어 조건부 컴파일: #ifdef

Cordilog 2026. 2. 24. 18:00

프로그래밍을 하다 보면 운영체제의 비트 수(32bit vs 64bit), 혹은 디버그 모드와 릴리스 모드 등 특정 개발 환경에 따라 코드를 다르게 실행해야 할 때가 있다.

이때 유용하게 사용할 수 있는 것이 바로 전처리기 지시자인 #ifdef이다.

#ifdef는 "if defined"의 약자로, 특정 매크로가 정의되어 있는지에 따라 코드의 컴파일 여부를 결정한다.

이를 통해 하나의 소스 코드로 다양한 환경에 대응하는 유연한 설계를 할 수 있다.

 

1. #ifdef의 기본 사용법과 동작 원리

#ifdef는 특정 매크로가 정의되어 있는지 확인하고, 정의되어 있다면 #endif를 만날 때까지의 코드를 컴파일 과정에 포함시킨다.

 

기본 문법:

#ifdef 매크로이름
    // 매크로가 정의되어 있을 때만 실행될 코드
#else
    // (선택 사항) 매크로가 정의되어 있지 않을 때 실행될 코드
#endif

컴파일러는 실제 컴파일에 들어가기 전 '전처리(Preprocessing)' 단계를 거친다.

이때 #ifdef 조건에 맞지 않는 코드 블록은 아예 삭제된 채로 컴파일러에게 전달된다.

결과적으로 최종 실행 파일의 크기를 줄이거나 환경별 최적화를 하는 데 유리하다.

2. 예제 코드 분석 

아래 코드는 포인터 배열을 선언하고, 시스템 환경(32bit 또는 64bit)에 따라 배열의 전체 크기를 출력하는 예제이다.

#include <stdio.h>

int main()
{
    // 포인터 배열 선언
    // 32bit: 4byte * 5 = 20byte
    // 64bit: 8byte * 5 = 40byte
    char *cbuf[5];  
    int  *ibuf[5];  

#ifdef D64bit
    // D64bit 매크로가 정의된 경우 컴파일됨
    printf("cbuf size: %ld \n", sizeof(cbuf));
    printf("ibuf size: %ld \n", sizeof(ibuf));
#endif

#ifdef D32bit
    // D32bit 매크로가 정의된 경우 컴파일됨
    printf("cbuf size: %d \n", sizeof(cbuf));
    printf("ibuf size: %d \n", sizeof(ibuf));
#endif

    return 0;
}

 

이 코드에서 핵심은 char *int *와 같은 포인터의 크기가 시스템 아키텍처에 따라 변한다는 점이다.

  • 32bit 시스템: 주소값을 저장하는 데 4바이트가 필요하다.
  • 64bit 시스템: 주소값을 저장하는 데 8바이트가 필요하다.

따라서 5개의 포인터를 가진 배열의 크기는 32bit에서 20바이트, 64bit에서 40바이트가 된다.

 

  • 포인터의 크기: 32비트 시스템에서는 4바이트, 64비트 시스템에서는 8바이트다.
  • 배열 전체 크기: * 32비트: 4바이트 × 5 = 20바이트
    • 64비트: 8바이트 × 5 = 40바이트

 

 

3. 컴파일 및 실행 결과 확인

매크로 정의는 코드 내에서 #define D64bit 처럼 할 수도 있지만, 보통은 컴파일러 옵션(-D)을 통해 외부에서 주입한다.

64비트 모드로 컴파일

-DD64bit 옵션으로 D64bit 매크로를 활성화한다.

$ gcc -Wall -DD64bit -o ex_ifdef ex_ifdef.c
$ ./ex_ifdef
cbuf size: 40 
ibuf size: 40

32비트 모드로 컴파일

-DD32bit 옵션과 32비트 빌드를 강제하는 -m32 옵션을 사용한다.

(사용자 설명에는 40바이트로 적혀 있었지만, 실제 32비트 환경에서는 주소값이 4바이트이므로 20바이트가 출력되는 것이 맞다.)

$ gcc -Wall -DD32bit -m32 -o ex_ifdef ex_ifdef.c
$ ./ex_ifdef
cbuf size: 20 
ibuf size: 20

 

4. 정리하며

#ifdef를 사용하면 하나의 소스 파일로 여러 타겟 환경을 지원할 수 있어 유지보수가 매우 편리해진다.

특히 임베디드 프로그래밍이나 시스템 프로그래밍처럼 하드웨어 의존성이 높은 분야에서 유용하다.

지금은 매크로가 정의된 경우만 다뤘지만, 반대로 정의되지 않았을 때를 체크하는 #ifndef도 있다는 것도 알아두자.