선행처리는 컴파일 이전의 처리를 의미한다.
일반적으로 컴파일의 과정에 포함이 되어 이야기하지만, 선행처리의 과정과 컴파일의 과정은 구분이 된다.
선행처리기가 하는 일의 예시를 보면
#define PI 3.14가 의미하는 것은 "PI를 3.14로 치환하라"이다.
컴파일러에 비해서 선행처리기의 역할은 매우 간단하다. '단순한 치환'의 작업을 거친다. 그리고 선행처리기에게 무엇인가를 명령하는 문장은 #으로 시작한다.
위에처럼 변수 대신 함수 형식으로 매크로를 만드는 방법으로 확장된다.
여기서 당연히 결과가 25로 나올거라고 생각하겠지만 결과는 11로 나왔다. 그 이유는 뭘까?
이유는 SQUARE(X) X*X일때 3+2*3+2로 되어서 11이된다.
그래서 이런 문제를 해결하기 위해서는 (X)*(X)로 해야한다.
하지만 이런 경우 num = 120/SQUARE(2)의 경우 120/(3+2)*(3+2)로 되어서 그대로 120이 나온다.
그래서 아래와 같이 수정을 해야한다.
매크로에 대해 좀 더 보면
두줄에 이어서 매크로를 정의하려면 \를 이용해야한다.
#define SQUARE(X) \
((X)*(X))
그리고 먼저 정의된 매크로를 다른 매크로를 정의하는데 사용할 수 있다.
일반함수와 비교했을 때 매크로 함수의 장점은 무엇일까?
- 매크로 함수는 일반 함수에 비해 실행속도가 빠르다.
- 함수의 호출을 완성하기 위해서는 별도의 메모리 공간이 필요하고 호출된 함수로의 이동 및 반환의 과정을 거쳐야 한다. 반면 매크로 함수는 정의된 몸체로 치환이 이뤄지니 이러한 일이 불필요하며, 때문에 실행속도가 빨라질 수 밖에 없다.
- 자료형에 따라서 별도로 함수를 정의하지 않아도 된다.
- 전달되는 인자의 자료형에 구분을 받지 않으므로 자료형에 의존적이지 않다.
단점은 무엇일까?
디버깅이 어렵고, 정의하기가 까다롭다.
왜냐하면 오류를 발견하는건 선행처리 후 컴파일러에 의해서 에러가 감지되기 때문에 디버깅이 어렵다.
그래서 함수를 매크로로 정의하는 것의 조건은 두 가지이다.
호출의 빈도수가 높고, 크기가 작은 간단한 함수이어야한다.
한 두줄 정도 크기의 작은 함수가 아니면 매크로로 정의하는 것이 쉽지 않아서 오류발생 확률이 높아진다. 그리고 if~else와 같이 실행의 흐름을 컨트롤 하는 문장도 매크로로 정의하기 쉽지 않다.
함수를 매크로로 정의하는 데에는 성능적 측면이 고려된다. 그런데 호출의 빈도수가 높지 않으면 애써 매크로로 함수를 정의하는 수고를 할 이유가 줄어든다.
#if #endif와 #ifdef #endif, #ifndef #endif의 세가지 경우로 조건을 사용할 수도 있다.
또한 #else를 추가할 수도 있다.
그리고 #if의 경우 #elif또한 사용할 수 있다.
이는 함수의 조건문과 비슷하기 때문에 쉽게 이해할 것이다.
이번에는 매크로로 문자열 내부의 매개변수를 치환하는 것에 대해서 알아보자
[문자열 치환 목적으로 정의된 매크로]
#define STRING_JOB(A, B) "A의 작업은 B입니다."
치환의 대상이 되는 A와 B가 문자열 안에 존재함에 주목하자!
[매크로의 확장 결과]
STRING_JOB(이동춘, 나무꾼) -> "A의 직업은 B입니다."
STRING_JOB(한상순, 사냥꾼) -> "A의 직업은 B입니다."
이동춘과 나무꾼, 그리고 한상순과 사냥꾼이 기대대로 치환되지 않았다. 문자열 안에서는 치환이 일어나지 않기 때문이다. 이럴때 고려해 볼 수 있는 연산자가 # 연산자이다!
문자열 내에서 매크로 매개변수 치환: #연산자
[# 연산자를 이용해서 정의된 매크로]
#define STR(ABC) #ABC
매개변수 ABC에 전달되는 인자를 문자열 “ABC”로 치환해라!
[# 연산자 기반의 매크로 확장 결과]
STR(123) -> "123"
STR(12,23,34) -> "12, 23, 34"
#연산자를 활용해서 이 문제를 해결할 수 있다.
그렇다면 문자열이 아닌 단순 연결은 어떨까?
여기에는 또 문제점이있다.
그래서 ##연산자로 이를 해결한다. 필요한 형태대로 단순결합을 할 수 있다.
[##연산자를 이용해서 정의된 매크로]
#define CON(UPP, LOW) UPP ## 00 ## LOW
매개변수 UPP와 LOW에 전달되는 인자를 UPP00LOW로 단순히 연결해서 치환해라!
[##연산자 기반의 매크로 확장 결과]
int num = CON(22,77); -> 220077
이렇듯 ## 연산자를 이용해서 다음과 같이 매크로를 정의해야 학번을 단순치환 할 수 있다.
#define STNUM(Y,S,P) Y ## S ## P
'CS 이론 > 학과 수업' 카테고리의 다른 글
논리회로 - 진수표현과 덧셈, 뺄셈, 보수 (1) | 2023.06.21 |
---|---|
파일의 분할과 다중 파일 컴파일 (0) | 2023.05.31 |
자료구조 undo 구현하기 (0) | 2023.04.23 |