'preprocessor'에 해당되는 글 2건

Programming/C C++
전처리란 소스코드를 컴파일하기전 소스코드에 특별한 처리를 행하는 것을 말합니다.

1. #include

소스에 헤더파일이나 기타 소스파일을 포함시킵니다. #include 를 사용하는 방법은 두가지가 있는데 하나는 <>를 사용하는 것으로 이 경우 해당 파일은 컴파일옵션에 미리 정해진 경로에서 파일을 찾게되며 ""는 현재 소스파일이 위치한 경로에서 지정한 파일을 찾게 됩니다. 이때 만약 파일을 찾지 못하면  없으면 <>와 같은 경로에서 파일을 찾게 됩니다.

2. #import

COM/ActiveX 개발시 자주 사용되는 것으로 형정보를 해당 라이브러리에서 가져오도록 합니다.

3. #using

.NET 관련 라이브러리를 참조합니다.

4. #if

다음 형식으로 사용되며
#if 조건
//내용
#elif 조건
//내용
#else
//내용
#endif
소스코드에 조건부(특정 플렛폼이나 언어등..) 컴파일을 지시합니다.

조건이 하나뿐인 경우 #elif 또는 #else는 생략할 수 있으며 여러줄의 내용을 포함하고 있더라도 일반적인 if문 처럼 {}로 묶을 필요가 없습니다.

5. #define

기본적으로 다음과 같이 상수를 정의하는데 사용됩니다.
#define MYVALUE 100
cout << MYVALUE << endl;
값으로는 100이나 200처럼 정수이외에 실수나 문자열형도 가능합니다.

#define는 상수를 정의하는 기능 이외에 함수자체를 정의하는 매크로 함수기능으로도 사용이 가능합니다.
#define PRINT(s) cout << s << endl
PRINT("hi!");

PRINT는 곧 위에 이은 cout 이하문과 동일하며 s는 매개변수부분에 해당합니다. 오해하지 말아야 할 부분은 PRINT자체인데 이것은 함수가 아닙니다. #define는 단순 전처리문에 불과하므로 PRINT는 컴파일전 cout 이하문으로 소스코드가 바뀌게 됩니다.

앞으로도 안내하는 모든 전처리문이 이와 같이 동작합니다. 소스코드를 바꿔주는 것일뿐 새로운 무엇인가를 만들어 주는 것이 아닙니다.

위에서 처럼 매개변수를 활용한 매크로함수를 구현하는 경우 #을 사용하면 전달되는 매개변수자체를 문자열로 취급할 수 있게 됩니다.

#define PRINT(s) cout << #s << endl

char* s = "hi!";
PRINT(s);

이 코드를 확인해 보면 문자 's'만이 출력되는데 전달되는 매개변수 s의 내용을 가져오는것이 아니라 #에 의해 변수자체를 그냥 값으로 취급해 버리기 때문입니다.

#을 ## 처럼 두개사용하면 매크로 정의된 내용에서 매개변수로 전달된 내용과 결합시키는 동작을 구현할 수도 있습니다.

#define type_declare(type, name) type my_##name

type_declare(int, value);

my_value = 100;
cout << my_value << endl;
type_declare는 두개의 매개변수를 받아 전처리 처리에 의해 type 이하 구문으로 바뀌게 됩니다. 이때 첫번째 매개변수는 type이며 int를 전달하면 type은 곧 int가 되고 name매개변수는 ##name으로 my_와 결합하여 새로운 식별자를 완성하게 됩니다.

단순한 상수를 정의하는 경우 const로도 처리할 수 있는데 #define와의 차이점은 const의 경우 데이터형을 엄격히 따지지만 #define는 데이터형을 별도로 따지지 않으므로 형검사면에서 #define이 좀더 취약한 면을 가지고 있다고 할 수 있습니다.

6. #undef

#define로 정의된 상수를 제거하는 역활을 합니다.
#define MYVALUE 100 //상수정의
cout << MYVALUE << endl; //출력
#undef MYVALUE //상수제거
cout << MYVALUE << endl; //오류 - 제거된 상수임
7. #ifdef / #ifndef

첫번째는 해당 식별자가 정의되어 있으면 true 이고 두번째는 식별자가 정의되어 있지 않으면 true를 수행하는 전처리문입니다.
#define MYVALUE 100

#ifdef MYVALUE
    cout << "상수정의됨" << endl;
#endif

#undef MYVALUE

#ifndef MYVALUE
   cout << "상수 정의되어 있지 않음" << endl;
#endif
0 0
Programming/C C++
C Program이 Compile시에는 실제 Source의 내용을 바로 Compile하는 것이 아니라 일단 개발자가 작성한 Source를 분석하여 필요시 일부 Source를 수정하는 작업을 진행 하게됩니다. 이처럼 Compile하기전에 미리 어떠한 작업이 이루어지는 것을 흔히 '전처리'라고 부릅니다.(전처리 과정은 대게 Source File과 include File을 결합한 직후 발생합니다.)

참고:
C언어는 일반적으로 ; 문자까지를 하나의 행으로 보지만 예외적으로 #로 시작하는 내용은 그 행의 끝까지를 하나의 행으로 간주합니다.

1. #include

Source Code의 처음부분에 #include <header file> 형식으로 선언되어 지정된 Header File을 Program의 Source Code에 포함되도록 합니다. 그러면 전처리를 통해 실제 Header File의 내용을 추가하는 작업이 이루어 지게 되는것입니다.

#include <stdio.h>

main()
{
  printf("hello\n");
}

▶Source 작성

/***
*stdio.h - definitions/declarations for standard I/O routines
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*       This file defines the structures, values, macros, and functions
*       used by the level 2 I/O ("standard I/O") routines.
*       [ANSI/System V]
*
*       [Public]
*
****/

#if     _MSC_VER > 1000
#pragma once
#endif
#ifndef _INC_STDIO
#define _INC_STDIO
#if     !defined(_WIN32)
#error ERROR: Only Win32 target supported!
#endif
--이하생략... 위 부분이 stdio.h의 실제 내용

main()
{
  printf("hello\n");
}


전처리에 의해 stdio.h Header File이 Program에 추가됩니다.

C에는 많은 Library의 Header File이 존재하지만 이 Header File을 개발자가 직접 작성여여 사용할 수도 있습니다. 이럴때는 #include를 통해 <와 >대신 "문자를 통해 사용자 Header File을 추가하면 됩니다.

#include "header file"

이때 해당 Header File은 현재 작업중인 경로에서 찾게 되는데 지정된 Hearder File이 없는 경우 다시 원래의 표준경로를 찾아가 Header File을 찾게 됩니다. 또한 사용자가 직접 작성한 Hearder File이라 하더라도 현재작업중인 경로가 아닌 표준경로에 저장된 경우 ""대신 <>로 Header File을 지정하여도 정상적으로 동작하게 됩니다.

myprint(char s[])
{
  printf("%s <- 문자열 출력\n", s);
}
▶'myheader.h' 라는 이름으로 Header File작성

#include <stdio.h>
#include "myheader.h"

main()
{
  myprint("hello");
}


myheader.h Header File을 현재 위치에 저장한 후 "로 myheader.h File을 포함합니다.


2. #define

전처리과정에서 #define로 정의된 문자열을 Source상에서 치환되도록 Macro를 정의합니다.

#include <stdio.h>
#define Count 10

main()
{
  int i;
 
  for (i=0;i<=Count;i++)
  {
    printf("i의 값 %d\n", i);
  }
}

▶ #define을 통해 Count라는 문자열을 정수 10으로 인식합니다.

#include <stdio.h>

main()
{
  int i;
 
  for (i=0;i<=10;i++)
  {
    printf("i의 값 %d\n", i);
  }
}


전처리를 통해 Source Code에 있는 Count라는 문자열을 10으로 치환한후 Compile합니다.


또한 #define를 사용하면 인수를 가져야 하는 함수자체를 정의할 수도 있어서 특정 함수를 간단히 활용할 수 있습니다.

#include <stdio.h>
#define prnt(s) printf("%s\n", s)

main()
{
  prnt("문자열 출력");
}


printf("%s\n", s)함수를 printf(s)로 Macro정의합니다. 이때 s는 화면에 출력할 문자열의 인수입니다.


Macro는 사용자가 정의할 수도 있지만 편의상 미리 마련된 Macro도 있습니다.

다음은 해당 Macro종류및 사용 예제입니다.

 macro  설명
 __DATE__  현재 날짜를 나타냅니다.(Computer설정 기준)
 __LINE__  _LINE_가 위치한 행의 번호를 나타냅니다.
 __TIME__  현재 시간을 나타냅니다.(Computer설정 기준)
 __FILE__  _FILE_이 있는 Source File의 이름을 나타냅니다.
 __STDC__  현재 C Program이 완벽히 ANSI C 표준만을 따를때 이 Macro가 정의됩니다.(값1)
만일 표준을 따르지 않고 현재 사용중인 개별 Compiler의 확장기능을 이용했을 경우 이 Macro는 정의되지 않습니다.

#include <stdio.h>

main()
{
  printf("현재 날짜는 %s 이며 시간은 %s 입니다.\n", __DATE__, __TIME__);
  printf("이 program file의 이름은 %s 입니다.\n", __FILE__);
  printf("이 행의 번호는 %d 입니다.\n", __LINE__);
  printf("ANSI C 표준입니까?%d\n", __STDC__);
}



Compile할때 /Za 를 지정하였습니다. ANSI C에 맞춰야 __STDC__ Macro를 확인할 수 있기 때문입니다. Microsoft C/C++ Compiler는 Compil시 /Za 가 있을때 해당 Source를 ANSI C에 맞게 Compile합니다.

3. #undef

#define를 통해서는 Macro를 정의하는 반면 #undef는 정의된 Macro를 해제하는 역활을 합니다.(#undef로 해제된 Macro는 #define로 다시 재정의 될 수 있습니다.)

#include <stdio.h>
#define line 500

main()
{
  int i;
  i = line;
  printf("현재 macro : %d\n", i);
 
  #undef line
  #define line 1000
 
  i = line;
 
  printf("재정의된 macro : %d\n", i);
}


main함수에서 #undef를 통해 미리 정의된 Macro를 해제하고 다시 #define로 Macro를 정의합니다.

주의:
#으로 시작하는 전처리는 끝에 ;문자를 붙이지 않습니다.


4. #if ~ #elif / #else ~ #endif

특정 조건을 만족할때만 개발자가 직접 작성한 Source Code의 일부분을 Compil하거나 또는 하지 않을 수 있습니다.(이것을 조건부 Compile이라고 합니다.)

#include <stdio.h>
#define error 0

main()
{
  #if (error == 0)
    printf("error는 0\n");
  #elif (error == 1)
    printf("error는 1\n");
  #else
    printf("이도저도 아님...\n");
  #endif
}


전처리에 의해 error가 정의된 값을 판단하여 해당 값에 따라 Compil하도록 합니다. 이것은 본래 if문과 작동은 비슷한 형태인데 error가 0이면 #if이하의 내용만, error가 1이면 #elif이하의 내용만 Compile하며 error가 0도 1도 아니면 #else이하 Code만을 Compile하도록 합니다.

실제 작동은 보통 if문을 쓸때와 별 다를바가 없지만 여기서는 Compile되는 Program에 어떤 Code의 내용만이 들어가는가가 중요합니다. 만일 #if를 쓰지않고 일반 if문을 쓰게되면 error가 무엇인지에 상관없이 모든 Code가 Compile될 것입니다.

#if는 전처리를 행한다는 특징때문에 Program이 실행되고 있다는 가정하에서는 아무런 소용이 없으며 일반적으로 Program Debug시에만 사용하곤 합니다.

5. #ifdef / #ifndef

Macro는 #define을 통해서 정의합니다. 이때 해당 Macro가 정의되었는가 또는 정의되지않았는가에 따라 별도의 처리를 하고자 한다면 #ifdef / #ifndef를 사용하도록 합니다.

#include <stdio.h>
#define error 10

main()
{
 #ifdef error
  printf("error가 10으로 정의됨\n");
 #endif
 #undef error
 #ifndef error
  printf("error가 정의되지 않음\n");
 #endif
}


#ifdef에 의해 error가 정의되어 있다고 판단되면 printf("error가 10으로 정의됨\n"); 부분을 Compile하게 되고 #undef에 의해 해당 Macro가 취소됩니다. 또한 #ifndef로 Macro미정의가 판단되어 printf("error가 정의되지 않음\n"); 부분이 Compile되는 것입니다.


6. #if ~ defined / !defined

앞서 살펴본 #ifdef는 단일 Macro만을 판단하지만 #if ~ defined(!defined)는 조건지시시자 개념으로 #ifdef를 보다 확장한 것이며 defined는 참일경우 !defined는 거짓일 경우 처리됩니다.

#include <stdio.h>
#define a 1
#define b 2

main()
{
  #if defined(a)
    printf("a 정의됨!\n");
  #endif
 
  #if !defined(c)
    printf("c 정의안됨!\n");
  #endif
 
  #if defined(b)
    printf("b 정의됨!\n");
  #endif
 
  #if defined(a) && defined(b)
    printf("a와 b둘다 정의됨.\n");
  #endif
}


#if ~ defined/!defined 는 위와같이 if조건연산자인 &&이나 ||등을 지정할 수 있습니다.


7. #error

조건에 따라 compile시에 Error Message를 표시하도록 합니다.

#include <stdio.h>
#define a 1

#if a == 1
#error a error...
#endif

main()
{
  printf("aaa\n");
}


#if ~ #endif 에 따라 a가 1이면 #error에 정의된 Message를 출력하도록 하였습니다.


8. ##

##은 이 ##을 기준으로 양쪽의 내용을 결합니다.

#include <stdio.h>
#include <string.h>

#define prnt(a, b) a ## b

main()
{
  printf("%s\n", prnt("hello", "world"));
}


prnt에 두개의 문자열을 , 로 분리하여 전달하지만 #define에서 a ## b문으로 "helloworld"내용으로 결합됩니다.

1 0
1
블로그 이미지

클리엘