본문 바로가기
IVS/C 프로그래밍

[C] 매크로(macro) 함수, 조건부 컴파일

by 코곰_ 2025. 1. 13.

 

매크로(macro)

  • C 언어에서 전처리기(preprocessor)가 처리하는 기능으로, 프로그램 실행 전에 텍스트 치환으로 동작.
  • C 언어에서는 #define 선행처리 지시문에 인수로 함수의 정의를 전달하여, 함수처럼 동작하는 매크로를 만들 수 있다. 
  • 매크로를 사용하면 코드의 재사용성과 가독성을 높이고, 반복적인 작업을 간단하게 처리할 수 있다.

 

매크로 함수의 장단점

 

👍 장점

1. 매크로 함수는 단순 치환만 해주므로, 인수의 타입을 신경쓰지 않는다.

2. 매크로 함수를 사용하면, 여러 개의 명령문을 동시에 포함할 수 있다.

3. 함수 호출에 의한 성능 저하가 일어나지 않으므로, 프로그램 실행 속도가 향상된다. (함수 호출과 달리, 런타임에 호출 비용이 없으므로)

 

👎 단점

1. 매크로는 단순 텍스트 치환이므로 연산자 우선순위를 명확히 지정하지 않으면 오류가 발생할 수 있다.

 

-> 따라서, 간단한 함수를 대체하는 데 좋다.

 

 

 

Ex1) 최대, 최소 정의

#include <stdio.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b)) 
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int main() {
    int num1 = 20; 
    int num2 = 30;
    
    int max_result = MAX(num1, num2);
    int min_result = MIN(num1, num2);

    printf("숫자 1: %d\n", num1); // 10
    printf("숫자 2: %d\n", num2);  // 30
    printf("최댓값: %d\n", max_result); // 30
    printf("최솟값: %d\n", min_result); // 20
    
    return 0;
}

 

 

 

단! 매크로 함수는 일반 함수와 달리 '단순 치환'만 하므로, 일반 함수와 완전히 같은 방식으로 동작하진 않는다. 
그로 이한 몇 가지 예시를 살펴보자. (Ex2- ①, ②)

 

EX2 - ) 합/ 차 정의

#include <stdio.h>
 
#define ADD(a, b) a+b
#define SUBTRACT(a, b) ((a)-(b))
int main() {
    int num1 = 20; 
    int num2 = 10;

    int sum_result = ADD(num1, num2) * 2;
    int diff_result = SUBTRACT(num1, num2);

    printf("숫자 1: %d\n", num1); // 20
    printf("숫자 2: %d\n", num2); // 10
    printf("덧셈 결과: %d\n", sum_result); // 40
    printf("뺄셈 결과: %d\n", diff_result); // 10

    return 0;
}

 

 

 

Ex2 - ) 합/ 차 정의

#include <stdio.h>
// 괄호를 과도하게 쳐서, function처럼 동작할 수 있도록 해야한다. 
#define ADD(a, b) (a+b)
#define SUBTRACT(a, b) ((a)-(b))
int main() {
    int num1 = 20; 
    int num2 = 10;

    int sum_result = ADD(num1, num2) * 2;
    int diff_result = SUBTRACT(num1, num2);

    printf("숫자 1: %d\n", num1); // 20
    printf("숫자 2: %d\n", num2); // 10
    printf("덧셈 결과: %d\n", sum_result); // 60
    printf("뺄셈 결과: %d\n", diff_result); // 10

    return 0;
}

 

Ex1, Ex2는 다르게 동작된다.

매크로를 작성할 때에는, 괄호를 적절히 쳐 function처럼 동작할 수 있도록 해야한다.

 

왜냐하면, 

Ex1에서는 int sum_result = (num1 + num2) * 2;

로 동작하기 때문에, (20 + 10) * 2 = 60

Ex2에서는 int sum_result = num1 + num2 * 2;

로 동작하기 때문에, 20 + 10 * 2 = 40 

 

 

 

Ex3)

#define SET_BIT(var, pos) ((var)|= (1 << (pos)))
#define CLEAR_BIT(var, pos) ((var) &= ~(1 << (pos)))
#define TOGGLE_BIT(var, pos) ((var) ^= (1 << (pos)))

 

 

매크로 함수는 일반 함수와 비슷해보이지만, 일반 함수와는 전혀 다른 시간대에 이루어지는 과정임을 기억!

 

 

 

 

 

 

 

 


#include <stdio.h>와 #include "stdint.h"의 차이

헤더 파일의 목적포함 경로에 따라 다르다.

 

#include <stdio.h> 
#include "stdint.h"

int main() {
    int temp = print(); 
    uint8_t temp2; 
    printf("%d\n",temp);
}

 

 

  1. #include <header>:
    • 컴파일러는 표준 라이브러리 경로에서 헤더 파일을 찾는다.
    • 주로 표준 헤더 파일을 포함할 때 사용합니다.
    • 예: <stdio.h>, <stdint.h>.
  2. #include "header":
    • 컴파일러는 현재 디렉토리에서 먼저 헤더 파일을 찾고, 없으면 표준 라이브러리 경로를 검색한다.
    • 주로 사용자 정의 헤더 파일을 포함할 때 사용합니다.

 

 

 

 

 

 

 

 

 


조건부 컴파일

  • 조건부 컴파일은 C 언어의 전처리기 지시자를 사용하여 특정 조건에 따라 소스 코드를 포함하거나 제외하는 기능
  • 코드의 일부를 컴파일하거나 무시하여 코드의 가독성, 유지보수성, 플랫폼별 코드 작성 등을 지원

 

1. #if

  • c언어의 if 조건문과 비슷
  • 단, #endif 지시자를 사용하여 반드시 조건부 컴파일의 끝을 명시해야 한다. 
#if 조건식1
    컴파일할 명령문1

#elif 조건식2
    컴파일할 명령문2

#else
    컴파일할 명령문3

#endif

 

2. #ifdef

  • 중복 선언의 가능성을 줄인다. = 'if defined'
  • #ifdef 지시자 다음으로 매크로 이름과 같은 이름의 매크로가 이미 정의되어 있으면, 컴파일할 명령문 1이 컴파일
  • 정의되어 있지 않으면, 컴파일할 명령문1이 컴파일되지 않고 넘어간다.
  • #elif, #else 지시자를 사용하여 컴파일 조건을 확장할 수 있다. 
#ifdef 매크로이름
    컴파일할 명령문1

#elif 조건식
    컴파일할 명령문2

#else
    컴파일할 명령문3

#endif

 

 

3. #ifndef

  • 'if not defined'
  • #ifndef 지시자 다음에 나오는 매크로 이름과 같은 이름의 매크로가 앞서 정의되어 있지 않으면, 컴파일할 명령문1이 컴파일
  • 정의되어 있으면, 컴파일할 명령문 1은 컴파일되지 않고 넘어간다.
  • 2와 마찬가지로 #elif, #else 지시자를 사용하여 컴파일 조건을 확장할 수 있다.
#ifndef 매크로이름
    컴파일할 명령문1

#elif 조건식
    컴파일할 명령문2

#else
    컴파일할 명령문3

#endif

 

 

 

 

예시

#include <stdio.h>

// 조건부 컴파일을 위한 매크로 정의
#define FEATURE_A

int main() {
    // FEATURE_A 매크로가 정의되어 있는 경우에만 해당 코드 블록이 컴파일됩니다.
    #ifdef FEATURE_A
    printf("이 코드는 FEATURE_A가 정의되어 있을 때만 컴파일됩니다.\n");
    #else
    printf("이 코드는 FEATURE_A가 정의되어 있지 않을 때만 컴파일됩니다.\n");
    #endif
    // 다른 조건부 컴파일 예제
    #ifndef FEATURE_B
    printf("이 코드는 FEATURE_B가 정의되어 있지 않을 때만 컴파일됩니다.\n");
    #endif
    // 두 개 이상의 조건을 조합하는 경우
    #if defined(FEATURE_A) && !defined(FEATURE_C)
    printf("이 코드는 FEATURE_A가 정의되어 있고 FEATURE_C가 정의되어 있지 않을 때만 컴파일됩니다.\n"); 
    #endif
    return 0;
}

 

실행결과

이 코드는 FEATURE_A가 정의되어 있을 때만 컴파일됩니다.
이 코드는 FEATURE_B가 정의되어 있지 않을 때만 컴파일됩니다.
이 코드는 FEATURE_A가 정의되어 있고 FEATURE_C가 정의되어 있지 않을 때만 컴파일됩니다.