2-5 전처리기

do·2022년 3월 21일
0

API

목록 보기
10/42

과제 - 1

  1. A_MAX, B_MAX, C_MAX 중 한 개의 값이 필요한 경우 해당 값이 정의된 헤더파일만 include 합니다.
  2. B_MAX, C_MAX 둘다 필요한 경우 header_b.h, header_c.h 둘다 include해서 컴파일이 되어야합니다.
/* header_a.h */
#ifndef HEADER_A_H	//헤더파일A가 여러번 포함될 예정이기 때문에, 헤더가드를 작성합니다.
#define HEADER_A_H
#define A_MAX 10
#endif

/* header_b.h */
#include "header_a.h"
#define B_MAX A_MAX+2

/* header_c.h */
#include "header_a.h"
#define C_MAX A_MAX+1

/* main.c */
#include <stdio.h>
#include "header_a.h"
#include "header_b.h"
#include "header_c.h"

int main()
{
	printf("A_MAX = %d\n", A_MAX);	//10
	printf("B_MAX = %d\n", B_MAX);	//12
	printf("C_MAX = %d\n", C_MAX);	//11
	return 0;
}

과제 - 2

  1. 2-4 처리 중간 결과에 대한 디버깅을 위한 디버깅 코드 추가하기 (내부 변수 내용 출력)
  2. 컴파일 옵션으로 디버깅 on/off 기능 추가하기
void Change(char* dest, char* src)
{
...
#if DEBUG=1 (#if DEBUG와 동일함)
	printf("%c ", *dest);
#endif
...
}

#define

  • 매크로를 정의할 때 사용하는 키워드
  • 컴파일 과정인 전처리기->컴파일러->어셈블러->링커 중 전처리기 단계에서 #define으로 정의된 기호 상수(기호를 이용하여 상수를 표기) 등을 소스코드로 대체시킵니다.
  • #define은 단순히 치환되는 형태이기 때문에, 매크로 함수를 호출할 때 인자 타입이 맞지 않아도 에러가 나지 않습니다.
  • 매크로 인자가 상수면, 컴파일 시점에 상수로 사용할 수 있습니다.
  • 매크로 인자가 변수면, 컴파일 시점에 상수로 사용할 수 없습니다.
  • 매크로 상수. 미리 정의한 매크로 상수명이 사용되면, 매크로 확장 문자열로 치환됩니다.
  • 매크로 함수. 매크로 상수와는 달리, 매크로 함수 이름에 괄호와 함께 인자 목록이 주어져있지만 단순히 치환하기만 하므로 실제 함수는 아닙니다. 인자로 받는 변수의 타입을 신경 쓰지않아 타입을 상관하지 않는 type-generic 함수를 구현하고자 할 때 매크로를 사용합니다.
  • 예제.
#define MUL(X,Y) ((X)*(Y))
#define PRINT(s) printf(#s)			//# 문자열로 변환시키는 연산자
#define CONCAT(X,Y,Z) X ## Y ## Z	//## 두 개의 토큰을 이어주는 연산자
#define TEMP(X) hello ## X()		//hello에 단어(문자,숫자)를 이어 호출하는 매크로

void hello1()
{
	PRINT(hello);
}

int main()
{
    int num = CONCAT(1,2,3);
	printf("%d\n", num);			//123 숫자 출력
	TEMP(1);						//hello1 함수 호출
    return 0;
}

#undef

  • 정의된 매크로를 해제할 때 사용하는 키워드
  • #ifndef와도 연동 가능

#include <파일명.h> vs #include "파일명.h"

  • 해당 헤더파일을 검색하는 순서의 차이
  • #include <파일명.h> (라이브러리에 정의된 헤더파일을 포함할 때) 컴파일러의 라이브러리 폴더를 검색합니다.
  • #include "파일명.h" 현재 소스가 존재하는 폴더를 처음에 검색하고, 찾는 헤더파일이 없을때 라이브러리 폴더를 검색합니다. 주로 사용자가 정의한 헤더파일을 포함할때 사용됩니다.
  • #include는 헤더파일이 아닌 cpp파일에 하는 것이 퍼포먼스가 더 좋습니다. 대용량 프로그램 작성시 컴파일 속도에서 많은 차이가 나타납니다.

#ifdef/#endif

#ifdef A	//A가 사전에 정의되었다면 문장a를 컴파일 합니다.
문장a
#endif

#ifndef/#endif

#ifndef B	//B가 사전에 정의되지 않았다면 문장b를 컴파일 합니다.
문장b
#endif

#if/#elif/#endif

#define A 0
#if A			//A에 들어가는 값이 중요하게 작용됩니다.
문장a
#elif num==B	//조건을 계속 분기할 수 있는 else if와 같은 역할.
문장b
#endif

#error

  • #error 메시지
  • 전처리기가 해당 메시지를 발견하면 컴파일이 중지되고 여기에 포함된 오류 메시지를 전달합니다.
void hello1()
{
#error This is an error.
}

//오류 발생
str.c: In function ‘hello1’:
str.c:11:2: error: #error This is an error.
   11 | #error This is an error.
      |  ^~~~~
make: *** [<builtin>: str.o] Error 1

조건부 컴파일

  • if~else문과 달리 #if~#elif~#endif 또는 #ifdef~#endif를 사용하게 되면, 조건부 컴파일이 가능합니다.
  • 조건을 쓸 때 주의할 점은, 조건에 0,1과 같은 상수는 사용할 수 있지만 변수를 사용하면 인식하지 못합니다. 따라서 매크로를 사용해야 합니다.
  • 디버깅과 관련해서 자주 사용되는 문법으로, 디버깅 시에는 특정 문자들을 수행하여 결과값을 출력해보는 문장을 넣어두고 컴파일 합니다. (프로그램의 동작상태를 런타임에 확인하는 용도일뿐, 실제 프로그램의 기능에는 영향을 미치지 않습니다.)
#define DEBUG 1 or 0
#if DEBUG==1
	printf("현재 num의 값은 %d입니다.\n", num);
#endif
#ifdef DEBUG
	printf("현재 num의 값은 %d입니다.\n", num);
#endif

헤더파일의 중복방지

  • 같은 헤더파일이 여러번 포함된 경우 문제가 생기기에, 헤더파일에 "이미 포함되었다"는 것을 의미하는 #define을 추가해야 합니다. (헤더가드, 인클루드가드)
#ifndef HEADER_H
#define HEADER_H
/*...contents of <header.h>...*/
#endif /* HEADER_H */

0개의 댓글