67 – 전처리기(조건부 컴파일)

특정 조건이 성립할 때나 조건이 성립하지 않을 때 지정한 범위 내의 문장을 컴파일하거나 무시하고 컴파일하지 않는 것이 바로 조건부 컴파일이다. 조건부 컴파일의 전처리기 지시자에는 #if, #elif, #else, #endif, #ifdef, #ifndef 등이 있다.

일단 제일 많이 사용되는 #if, #elif, #else, #endif를 보자. 형식은 아래와 같으며, 수식 1이 참이면 실행 1을 컴파일하고, 수식 1이 거짓이고 수식 2가 참이면 실행 2를 컴파일한다. 모든 수식이 거짓이면 실행 n을 실행한다.

#if 수식 1
실행 1
#elif 수식 2
실행 2
……
#else
실행 n
#endif

if, else if의 사용과 같은 느낌이 들지 않는가? 사용이 똑같다. 전처리가 되어 있다는 것으로 인해 컴파일하는 코드의 길이가 달라질 수 있기 때문에 특정 환경, 특정 옵션에 맞는 프로그램을 개발하고자 할 때 많이 쓴다. C로 만들어진 프로그램에서 설치 옵션이나 실행 옵션 등이 있는 경우에 많이 이용하고, 운영체제 같은 큰 프로그램을 짜면 반드시 보게 될 것이다.

이건 아래의 예시를 보면 더 쉽다. 설명하지 않은 #ifndef가 있는데, 이건 정의되지 않았을 경우를 나타낸다. NAME이 정의되어 있기 때문에 NAME이 정의되었다는 문장이 출력되고 ADDRESS는 정의되지 않았기 때문에 #ifndef에 맞춰 정의되지 않았다는 문장이 출력된다.

스크린샷 2017-04-21 오후 4.43.12

66 – 전처리기(파일 삽입)

파일 삽입 기능은 그 위치에 다른 파일을 삽입하기 위해 사용되는 기능이다. 우리는 매번 썼다. #include라고 매번 작성하였다. 이 작성문은 두 가지 형태고 작성된다.

#include <파일이름>
#include “파일이름”

우선, <파일이름>이라고 지정된 것은 해당 파일을 시스템 디렉토리에서 찾는다. 반면에 “파일이름”이라고 지정하면 파일을 우선 현재 작업하고 있는 디렉토리에서 찾고, 없을 경우에는 시스템 디렉토리에서 찾는다. 그래서 귀찮으면 그냥 “파일이름”이라고 통일해서 적기도 한다.

파일 이름에는 대부분 h라는 확장자를 붙이며, 이것을 헤더 파일(header file)이라고 한다. 그러면 이에 대한 명확한 이해를 돕기 위해 예시를 작성하였다. 예시를 보고 나서 설명을 하도록 하겠다.

<stdio.h> 헤더 파일은 사용자의 편의를 위해 시스템에서 미리 만들어 제공하는 파일로써 누구나 이용할 수 있는 파일이다. 반면 “my.h”과 같은 파일은 사용자가 직접 작성한 파일이다. my.h 파일을 아래와 같이 작성하였다.

다른 파일에서도 my.h를 파일 삽입으로 삽입하면 저기 정의된 것들을 그대로 쓸 수 있다.

전처리기로 파일 삽입을 진행하였기 때문에 컴파일 할 때에는 별도로 여러 파일을 정의하지 않아도 된다. 기존의 예시에 gcc [파일] [파일] 과 같이 해서 두 개 이상의 c 파일을 컴파일 한 적이 있는데, 이건 아니다.

65 – 전처리기(매크로 정의)

#define을 이용하여 기호 상수 PI를 정의하는 것을 이전에 한 번 예제로 보여줬는데, 바로 그것이 매크로다. 즉, 프로그램을 작성할 때 반복 기술되는 명령어나 수식이 사용자가 별도의 이름을 부여할 수 있는 기능이 매크로다. 매크로에는 매개변수를 사용하지 않는 경우와 매개변수를 사용하는 경우가 있다. 정의 형식이 다른데, 형식은 다음과 같다.

#define 매크로이름 치환문자열
#define 매크로이름(매개변수들) 매개변수를포함하는치환문자열

배개변수를 사용하는 매크로는 예시를 보면 좀 알 수 있을 거 같다.

#define ADD(A, B) ((A) + (B))

ADD는 매크로 이름, A랑 B는 매개변수, ((A) + (B))는 치환 문자열이다. 여기서 주의할 사항은 매크로 이름인 ADD와 매개변수의 목록인 A,B 사이를 띄우면 안된다는 점이다.

일단 예시를 보도록 하자. 매개변수가 없는 매크로의 예시는 다음과 같다.

전처리를 하여 문장 중의 매크로가 해당 문자열로 바뀐 상황이다.

다음은 매개변수가 있는 매크로를 이용해 두 수 중에 큰 수를 구하는 프로그램이다.

매개변수가 있는 매크로는 함수와 거의 유사하지만, 컴파일 할 때 링크 과정을 거치지 않아도 되므로 함수를 불러오는 시간이 절약되는 장점이 있다.

이런 매크로를 정의하는 지시자뿐만 아니라 이미 정의된 매크로를 해제하는 지시자도 있는데, 바로 #undef문이다. 예시를 보도록 하자. 먼저 정의했던 SIZE 매크로를 실행 도중에 #undef로 해제한 다음에 다시 #define으로 정의하는 코드가 있다. 그로 인해 실행 결과가 바뀐다.

64 – 전처리기

사용자가 작성한 프로그램을 실행하기 위해서는 컴파일러로 소스 파일을 실행 파일로 변환해야 한다. C언어에서는 컴파일하기에 앞서 #으로 시작되는 전처리기 지시자 부분을 먼저 처리한다. 이러한 일을 하는 것이 전처리기(preprocessor)로, 프로그램에서 사용자 임의로 수식이나 상수를 정의하거나 외부 파일을 삽입하는 등의 작업을 한다.

전처리해야 하는 전처리기 부분은 #으로 시작하고 문장 끝에는 세미콜론(;)을 붙이지 않는데, 이런 전처리기는 어떤 종류가 있는지, 정의는 어떻게 하는지를 정리해 나가도록 하겠다.

63 – 공용체

공용체는 구조체와 형식이 동일하지만 구조체와 달리 공용체 내에 있는 각 맴버가 동일한 기억장소를 공유하면서 이를 경우에 따라 다른 용도로 사용한다. 그러므로 공용체는 필요한 유형의 변수에 따라 다르게 사용될 수 있지만 한순간에 하나의 맴버만 사용할 수 있다.

union position {
int class;
char department[10];
};

공용체는 이 예처럼 union 키워드를 이용하여 만든다. position은 태그 이름이고, class와 department라는 두 개의 맴버로 구성되어 있다. 선언하는 방식도 구조체 선언하듯이 하면 된다.

union position my;

그리고 공용체도 구조체처럼 정의와 변수 선언이 동시에 가능하다.

여기서 차이가 어떤 것이 있는지를 보여주도록 하겠다. 동일한 구조의 구조체가 있다고 하면, 해당 구조체의 구조가 어떤 식으로 다른 구조로 되어있는지를 보여주면 다음과 같다.

공용체형으로 만들게 되면 구조체형과 달리 동일한 기억장소에 공유하게 되어 두 개의 변수 중 하나만을 사용할 수 있도록 되어 있다. 그에 반해 구조체에는 두 개의 변수를 둘 다 별도의 기억장소에 저장하게 되며, 둘 다 사용할 수 있다.

이런 공용체를 만드는 이유가 있다. 공용체는 한정된 기억장소를 절약하기 위해 사용되는 구조이다. 즉, 값으로 들어가는 맴버가 여러 형태가 존재하나 그 여러 맴버가 동시에 사용될 일이 없는 구조에 이용된다. 이걸 예시로 만들어본 것이 아래의 예제이다. 고등학생일 경우에는 사실 반으로 따지지만 대학생이라면 전공으로 따지게 된다. 그런 것을 예시로 하여 직접 예시를 작성해봤다. 단, 값은 어떤 것이 들어가도 된다는 걸 보여주기 위해 임의로 막 넣었다.