선행처리는 우리가 소스 코드상 맨 위에 적어 놓은 # 명령어를 수행하는 과정이다.
대부분 단순 치환으로 이루어져 있다.
예를 들면 #include <stdio.h> 는
stdio.h 파일의 내용을 소스 코드 안에 붙여넣는 일을 한다.
#define A B 는
매크로 A 를 매크로 몸체(대체 리스트)인 B로 치환을 한다.
이 #define 명령어에 대해 자세히 알아보자.
#define A B 는 두 가지 형태로 사용이 가능한데,
첫번째는 Object-like macro 가 있다.
이런 형태는 변수 선언하듯이 사용해서 상수처럼 사용을 한다.
#define PI 3.14
int a = PI;
위와 같은 코드에서 선행처리 과정을 거치면 모든 PI 들이 3.14로 대체된다. 이런 의미에서 상수와 같다고 본다.
그리고 두번째 형태는
Function-like macro 이다.
이는 함수처럼 매개변수를 활용하여 사용한다.
#define SQURE(x) x*x
int a = SQURE(5);
위의 코드블럭에 나와 있는 SQURE(x) 는 전부 선행 처리 과정에서 x*x 로 치환된다.
따라서 int a = 5*5 로 바뀌게 된다.
이처럼 함수와 유사하게 활용 가능하다.
이러한 형태를 매크로 함수라고도 하며 이 변환 과정을 매크로 확장이라고 한다.
--------------------------------
기본 사용법
다만 위에서 유사하게 활용이 가능하다고 적었듯이,
이 매크로들은 유도리있는 사용이 안되고 무조건 정직하게 치환만 해준다.
이게 무슨 뜻이냐면, 바로 위에 있는 SUQRE 매크로 함수의 매개 변수에 1+2 를 넣어서
SUQRE(1+2) 라는 코드를 작성했다면 이는 선행 처리 과정 후 어떻게 바뀔까?
센스있게 알아서 3*3 으로 치환시켜줬으면 좋겠지만
매크로는 아주 정직하게 1+2 * 1+2 로 치환을 하여 결과값이 5가 된다.
이외에도 본문에 복잡한 연산들과 함께 섞어서 쓰는 경우
10/SQURE(2+6)+SQURE(2)..... 이런 식으로 마구잡이로 사용하게 되면
단순 치환이 된 후에는 의도하지 않은 결과를 낼 수 있다.
이를 방지하려면 대체 리스트를 괄호로 적절하게 묶어 사용하면 된다.
#define SQURE(A) (A)*(A)
//혹은 더욱 완전한 형태로
#define SQURE(A) ((A)*(A))
위와 같이 묶어주면 된다고 한다. 그래서 사용 시에는 웬만하면 제일 마지막에 정의된 것 처럼
최대한 괄호를 칠 수 있는만큼 쳐서 의도치 않은 오류를 방지하도록 하자.
또한 매크로가 길어져서 두 줄 이상으로 사용하고 싶다면 \(백슬래시) 를 마지막에 넣어줘서 줄바꿈을 명시해주면 되며,
먼저 정의된 매크로를 새로 만들 매크로의 정의에 사용할 수 있다.
그렇다면 어떠한 함수를 매크로로 정의해야 할까?
매크로 함수는 실행 속도가 빠르고 자료형에 따른 별도의 정의가 필요 없다.
다만 아래에서도 다루겠지만 정의하기가 상당히 까다롭고 디버깅하기 쉽지 않다.
그래서 소규모의 함수와 호출 빈도가 높은 함수를 매크로로 정의하는 것이
바람직한 활용법이라 할 수 있다.
---------------------------------------------
조건부 컴파일을 위한 매크로
3가지의 문법이 있다.
아래 코드블럭을 참고.
#if A // 매크로 A가 참이라면 endif 까지의 코드를 실행
/*
.
*/
#endif
/////////////////////////////////////////////////////////////
#ifdef A // 매크로 A가 정의되어 있다면 endif 까지의 코드를 실행
/*
.
*/
#endif
/////////////////////////////////////////////////////////////
#if A // 매크로 A가 참이라면 endif 코드를 실행
/*
.
*/
#elif B // 혹은 B가 참이라면 코드를 실행 => else if라고 생각하면 된다
/*
.
*/
#else // 전부 아니면 코드를 실행 => else 라고 생각하면 된다.
/*
.
*/
#endif
C언어에서의 if, else 와 같다. 다른 점은 마지막에 #endif가 들어가야만 한다.
---------------------------------------------------------------
매개변수의 결합과 문자열화
이는 과연 앞으로 쓰일 일이 있을지 잘 모르겠어서(사실 지금까지 #명령어를 라이브러리 관련 제외하면 써본적이 없다) 일단은 읽어두기만 했다.
내용은 간략히만 요약하기로 함.
매크로에서 문자열을 사용하기 쉽게 만들어주는 연산자로 #가 있다.
#가 대체 리스트에 쓰이게 되면 문자열로 취급해주는 것 같다.
#define STR(ABC) #ABC
책에서의 예시는 이렇게 나와 있다. ABC를 매개변수로 받으면 이를 문자열 ABC로 취급한다는 뜻이다.
## 연산자 또한 존재하는데 이는 여러 매개 변수의 인자들을 다른 대상과 이어줄때 사용한다고 한다.
#define STR(A, B, C) ABC // 이 경우 A, B, C가 따로따로 인식되지 않고 하나의 ABC로 인식됨
#define STR(A, B, C) A ## B ## C // 이렇게 하면 A, B, C 각각 다른 인자로 인식됨
위와 같은 상황에서 쓰인다고 한다.
---------------------------------------------
문제 26-1
문제 1
덧셈은 쉽게 짤 수 있어 보이나, 곱셈 부분에서 괄호를 적절히 쳐 줘야 할 것 같음.
#include <stdio.h>
#define ADD(A, B, C) ((A)+(B)+(C))
#define MUL(A, B, C) ((A)*(B)*(C))
int main(void)
{
printf("ADD : %d\n", ADD(2, 4, 6));
printf("MUL : %d\n", MUL(2, 4, 6));
return 0;
}
문제 2
PI 를 먼저 정의한 뒤 매크로 함수를 작성한다.
#include <stdio.h>
#define PI (3.14)
#define AREA(R) ((PI)*(R)*(R))
int main(void)
{
printf("AREA : %G\n", AREA(5));
return 0;
}
문제 3
조건 연산자를 이용하면 될 듯.
#include <stdio.h>
#define MAX(A, B) ((A) >= (B) ? (A) : (B))
int main(void)
{
printf("MAX : %d", MAX(5, 10));
return 0;
}
오늘은 여기까지.
'프로그래밍 > C*' 카테고리의 다른 글
(C) 파일의 분할 / 헤더파일 디자인 (0) | 2022.03.05 |
---|---|
C에서 메모리의 동적 할당 (0) | 2022.03.01 |
댓글