프로그래밍을 하다 보면 운영체제의 비트 수(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도 있다는 것도 알아두자.
'Journey to Security > C언어' 카테고리의 다른 글
| C언어 3중 포인터와 문자열 배열의 메모리 구조 (0) | 2026.02.24 |
|---|---|
| C 언어 배열의 문자열 수정과 strcpy의 동작 원리 (+strncpy) (0) | 2026.02.24 |
| GDB TUI 모드: 소스 코드를 보며 디버깅하기 (0) | 2026.02.23 |
| C 포인터 배열과 이중 포인터의 메모리 구조 (0) | 2026.02.23 |
| C언어 배열명과 포인터의 차이: GDB로 문자열 저장 방식 뜯어보기 (0) | 2026.02.22 |