[C]매크로(mecro)


이번 포스트는 매크로(mecro)에 관한 내용입니다.


1️⃣ #define & #undef

(1) #define

#define  식별자 ( 대체_목록 )


  • 전처리과정에서 식별자가 보이면 모두 대체_목록로 바꿔줍니다.
  • 대체_목록

    없이 #define  식별자으로 쓰이면 내용으로 바꿔주지 않고 단순히 정의(define)만 됩니다.( 인클루드 가드에 사용)


(2) #undef

#undef  식별자


  • 이미 정의된 식별자가 있을 경우 없애 줍니다.

< 정의 매크로 예시 >

/* A가 정의되어있지 않으면 정의1 */
#ifndef A
# define A (0)
#endif

/* A가 정의되어있지 않으면 정의2 */
#if !defined (A)
# define A (0)
#endif

/* A가 정의되어있다면 정의를 해제1 */
#ifdef A
# undef A
#endif

/* A가 정의되어있다면 정의를 해제2 */
#if definde(A)
# undef A
#endif

/* 인클루드 가드 */
#ifndef TEST_H
# define TEST_H
..헤더 내용..
#endif

(3) 미리 정의되어 있는 #define예

  • C언어에는 미리 정의되어 있는 매크로들이 있습니다.
  • __FILE__: 현재 파일의 이름을 문자열로 표시합니다.
  • __LINE__: 현재 코드의 줄 번호를 정수형으로 표시합니다.

< 오류 출력시 사용 >

err_macro_example /*---출력---*/
error: test.c, line 5.




2️⃣ 조건부 컴파일

(1) 조건부 컴파일

  • 대표적인 예시로 unist.h헤더의 경우 윈도우운영체제에서는 io.h헤더로 선언되어 있습니다.
  • _WIN3232, 64비트 윈도우운영체제를 뜻합니다.
  • 즉, 다음과 같이 윈도우운영체제일 경우 io.h헤더를, 아닐 경우 unistd.h헤더를 호출합니다.
#if defined(_WIN32)
# include <io.h>
#else
# include <unistd.h>
#endif

(2) 인클루드 가드로 이용

< one.h >

#include "two.h"
/* one헤더파일 코드 생략 */

< two.h >

#include "one.h"
/* two헤더파일 코드 생략 */

< test.c >

#include "one.h"

int main(void)
{
	printf("Hello world!\n");
	return (0);
}

위의 세파일을 만들고 test.c파일을 전처리 단계만을 거치면 다음과 같이 무한으로 헤더가 확장됩니다.

< 전처리 단계를 거친후 test.c >

           ...
           ...
/* one헤더파일 코드 생략 */
/* two헤더파일 코드 생략 */
/* one헤더파일 코드 생략 */
           ...
           ...
/* two헤더파일 코드 생략 */
/* one헤더파일 코드 생략 */
/* two헤더파일 코드 생략 */
/* one헤더파일 코드 생략 */

int main(void)
{
	printf("Hello world!\n");
	return (0);
}
  • one.h가 two.h를 복붙 -> two.h가 one.h을 복붙…을 끝도 없이 반복하게 됩니다.(컴파일러가 강제로 멈춤)
  • 보통 이것을 순환 헤더 인클루드(circular header include)라고 합니다.(“헤더가 꼬였다”)
  • 그렇기 때문에 #include는 되도록이면 .c에서만 하는 것이 좋습니다. 하지만 어쩔 수없이 헤더 파일을 서로 인클루드해야 할 일이 있습니다.
  • 이러한 순환 헤더 인클루드#ifndef를 사용하여 방지할 수 있습니다.

(2) 이중헤더방지 매크로(인클루드 가드) 구현 예

#ifndef  TEST_H
# define  TEST_H
/*
코드 생략
*/
#endif




3️⃣ 기타 이용


(1) 주석으로 이용

  • 매크로를 이용하여 주석처리를 할 수 있습니다.
  • /* */를 이용하여 주석을 처리할 수 있지만 주석을 지우고 쓰고를 반복할 때 시작과 끝을 까먹을 때가 있습니다.
  • 또한 /* */를 이용한 주석처리는 중첩이 안됩니다.

< 매크로 주석 사용 >

err_macro_example

< 매크로 주석 해제 >

err_macro_example


(2) #error(에러출력 매크로)

#error  메세지


  • 컴파일 도중에 강제로 오류를 발생시키는 매크로입니다.
  • 버전이 다르면, 정의가 안되어 있으면, 아직구현안한 운영체제환경이면 등등 컴파일을 못하게 하고 에러를 출력합니다.
  • 메세지를 꼭 큰따옴표로 감쌀 필요는 없습니다.

< 버전관리 사용예 >

/* version.h */
#define  VERSION  20

/* program.h */
#if  VERSION  !=  10
#error  "This version is not supported."
#endif
/* 출력 */
test.c:6:2: error: #error "This version is not supported."
#error "This version is not supported."
 ^~~~~


< 운영체제 사용예 >

#ifndef  _WIN32
#error  "Not windows os."
#endif
/* 출력 */
test.c:6:2: error: #error "Not windows os."
#error "Not windows os."
 ^~~~~




4️⃣ 컴파일 중에 매크로 정의하기('-D' 옵션)


(1) A값 정의

< base.c 파일 >

#include <stdio.h>

int main(void)
{
    printf("%d", A);   // A가 선언되어 있지않음
}

< -D옵션 사용1 >

$> gcc base.c -DA
$> ./a.out
/* 출력 */
1
  • 단순히 -DA로 작성하면 #define A (1)과 같이 적용됩니다. (#define A가 아님)

< -D옵션 사용2(숫자지정) >

$> gcc base.c -DA=42
$> ./a.out
/* 출력 */
42
  • #define A (42)와 같은 결과가 출력됩니다.

(2) 배포용으로 컴파일하기 ('-D' + 'NDEBUG')

  • 배포(release)모드 실행파일을 컴파일해달라는 매크로 입니다.
    • NDEBUG는 ‘디버그가 아니다’라는 뜻입니다.
    • assert()가 사라집니다.
    • 디버그 모드에서만 실행될 코드#if !defined(NDEBUG) 속에 넣습니다.
  • 추가적으로 다음과 같은 매크로를 직접 정의(자기만의 매크로명)해 사용하는 프로젝트들도 많습니다.
    • DEBUG: 디버그용 빌드
    • RELEASE: 배포용 빌드
    • 등등.. 이런식으로 자기만의 매크로명으로 다양한 빌드를 지정할 수 있습니다.




© 2021.02. by kirim

Powered by kkrim