728x90
반응형

매크로 선행 처리기

  • 선행 처리기

선행 처리 과정은 프로그래머가 삽입해 놓은 선행 처리 명령문 대로 소스 코드의 일부를 수정한다. 수정은 단순 치환 형태를 띠는 것이 대부분이다. 다음은 간단한 선행 처리에 대한 예제이다.

위의 예에서 선행 처리 명령문은 2번 라인과 4번 라인, 두 가지가 존재한다. 선행 처리 명령문은 # 문자로 시작하고, 명령의 끝에는 세미콜론을 붙이지 않으며 컴파일 이전에 컴파일러가 아닌 선행 처리기에 의해 처리된다. 먼저, 4번 라인의 경우, PI라는 기호를 만나면 3.1412로 치환해라 라는 의미이다. 이 때 PI 기호를 만난다는 것은 쌍 따옴표로 둘러 쌓인 문자열 내의 PI가 아닌 모든 PI 기호를 이야기한다. 선행 처리기에 의해 선행 처리가 진행되면 오른쪽 그림과 같이 치환이 된다. 그리고 2번 라인에서 헤더 파일을 삽입하는 것 또한 선행 처리기에 의해 진행된다. stdio.h라는 파일의 내용을 include 명령이 쓰인 해당 파일에 가져다 놓으라는 의미를 가지고 있다.

 

  • Object-like macro(오브젝트와 유사한 매크로, 매크로 상수) : #define

    지시자 매크로 매크로_몸체
#define   PI   3.1412

지시자는 #include, #define 등과 같은 것을 가리켜 지시자라고 한다. 그리고 이어서 매크로라고 하는 기호와 대치되어야 할 매크로의 몸체가 등장한다. 선행 처리기는 이 부분을 보고 프로그래머가 지시하는 바를 파악한다. 그 중 #define 지시자는 다음과 같은 내용을 지시한다.

이어서 등장하는 매크로를 마지막에 등장하는 매크로 몸체로 치환!

다음은 이에 대한 예이다.

4번 라인부터 6번 라인까지 매크로 상수를 정의 및 지시가 이루어 졌고, 10번 라인부터 12번 라인에서 컴파일 이전에 치환(대치)이 이루어진다. 특이한 점은 6번 라인처럼 함수의 호출 자체 또한 매크로 몸체로 올 수 있다는 점이다.

 

  • Function-like macro(함수와 유사한 매크로, 매크로 함수) : #define

형태는 매크로 상수와 동일하다. 다음은 매크로 함수의 예이다.

지시자      매크로    매크로_몸체
#define    SQUARE(X)        X*X

기존 매크로 상수에 괄호를 등장시킴으로써 함수의 형태를 보이고 치환을 진행할 수 있다. 여기서 X는 정해 지지않은 임의의 값 또는 문장을 이야기 하는데 어떤 문자로 든 올 수 있으나 매크로와 매크로_몸체에서 사용되는 문자는 같은 문자로 통일 되어야 한다. 위의 예에서 해석을 해보자면 SQUARE(X)와 같은 패턴이 등장하게 되면 X*X 형태로 바꿔라 라는 의미다. 함수 호출과 매우 유사하고, 이렇게 변환되는 과정 자체를 매크로 확장이라고 한다. 다음은 관련 예제이다.

 

  • 매크로 함수의 문제(우선순위)

위의 예제는 매크로 함수의 잘못된 사용의 결과이다. 3+2 값의 Square 연산은 5^2으로 25가 나와야 하지만 전혀 다른 결과가 나왔다. 그 이유는 매크로 함수 치환을 풀어보면 나타난다. 3+2*3+2 형태로 치환이 되었기 때문에 우선 순위 연산에 의해서 곱셈 연산자가 먼저 연산 되는 바람에 11이라는 값이 나왔다. 이를 해결하기 위해서는 매크로 함수 정의 시 괄호를 치고 구분을 해주어야 한다.

위와 같은 정의로 매크로 함수를 구성하면 정확한 결과를 얻을 수 있다.

 

  • 매크로 기타 사항

먼저, 매크로 몸체가 복잡해서 두 줄 이상에 걸쳐서 정의를 할 경우에는 두 줄에 걸쳐서 매크로를 정의해야 하는 경우도 생기는데 이 때 그냥 임의로 줄을 변경하면 에러가 발생한다. 이럴 때에는 \문자를 이용해서 줄이 바뀐 매크로 정의임을 명시한다.

그리고 또한 이미 정의한 매크로를 이용하여 또 다른 매크로 정의가 가능하다. , 매크로 정의 시, 먼저 정의된 매크로도 포함 및 사용이 가능하다.

 

 

  • 매크로 함수의 장점과 단점

 

매크로 함수는 보통 작은 크기의 함수 / 호출의 빈도 수가 높은 함수를 매크로 함수로 보통 정의한다. 함수의 크기가 작아야 에러의 발생 확률 또한 낮고, 호출의 빈도 수가 높아야 매크로 함수가 가져다주는 성능 상의 이점을 최대한 활용할 수 있다.

 

 

  • 조건부 컴파일

#if ~ #endif : 참이라면

조건부 컴파일을 이용한 예이다. 7번의 매크로 상수를 FALSE로 정의하면 12번 라인은 실행하지 않는다.

#ifdef ~ #endif : 정의되었다면

12번 라인에서 ifdef는 해당 매크로 상수가 정의되어 있으면 ifdef 내부를 실행한다. 그리고 13번 라인처럼 비교 연산 또한 가능하다. 그리고 8번 라인처럼 공백으로 매크로 상수를 정의할 수 있다. 이 경우에는 선행 처리 과정에서 공백으로 대체되므로 소멸 의미와 같다.

 

  • #elif의 삽입 : #if만 가능

#elif를 통해서 else if 와 같은 효과를 #else를 통해서 else와 같은 효과를 볼 수 있다.

 

  •  매개 변수의 결합과 문자열화

다음과 같은 문장이 가능한지 보자.

#define CONCATENATE(A,B) “A B”

위와 같은 문장은 일단 불가능하다 왜냐하면 문자열 안에서는 매크로의 매개변수 치환이 발생하지 않기 때문이다. 이러한 경우 # 연산자를 이용한다.

#define STR(A) #A

위 문장의 의미는 A로 전달되는 인자를 문자열 “A의 내용형식으로 치환하라는 의미이다.

따옴표를 넣고 안 넣고 의 차이를 확인한다.            

이런 치환 또한 가능하다. 9번 라인에서 두 문자열을 따로 썼지만 합쳐진 결과가 나타났다. 이를 활용해보면.

#define STRCAT(A,B,C) ABC

위의 경우 STRCAT(YOO, SEUNG, HO);라고 하면 “YOO SEUNG HO”라는 문자열이 조합될 것 같지만, 실제로는 조합되지 않는다. 왜냐하면 ABC ABC의 문자열 결합으로 보지 않고 ABC를 하나의 새로운 매크로 상수로 보기 때문이다. 이 경우 ABC로 그냥 치환이 될 뿐이다.

#define STRCAT(A,B,C) A B C

이 경우에는 STRCAT(as,vf,sd); 라고 호출될 경우 as vf sd 로 치환이 될 뿐이다.

이러한 문제를 해결하기 위해서는 ##연산자를 사용해야 한다.

 

  • 필요한 형태로 단순 결합 : 매크로 ## 연산자

## 연산자를 통해 인자로 전달된 요소들을 단순 결합을 시킨다. 다음은 그에 따른 예제이다.

합쳐진 것을 볼 수 있다.

참고로 소스 코드 위에 단순 치환이기 때문에 AB라는 문자열과 1234라는 문자열을 치 ## 연산할 경우 AB1234라는 문자열이 생기는 것이 아니라 소스 코드 상에 AB1234로 대치가 되는 것이다 그렇기 때문에 AB1234는 소스 코드 상에서 변수나 함수 등의 이름으로 인지될 것이고, 이는 없는 이름이기 때문에 에러가 발생한다. 이 점을 혼동하지 않아야 한다.

728x90
반응형

'컴퓨터 언어 정리 > C 언어' 카테고리의 다른 글

20 파일의 분할  (0) 2020.10.17
18 동적 메모리 할당  (0) 2020.10.15
17 파일 입출력  (0) 2020.10.13
16 구조체  (0) 2020.10.12
15 문자와 문자열  (0) 2020.09.29

+ Recent posts