Wednesday, March 26, 2014

Program: UML

http://stackoverflow.com/questions/15376/whats-the-best-uml-diagramming-tool

http://en.wikipedia.org/wiki/List_of_UML_tools

Python: Can dependency injection prevent a circular dependency?

Can dependency injection prevent a circular dependency?
http://stackoverflow.com/questions/2053044/can-dependency-injection-prevent-a-circular-dependency

http://stackoverflow.com/questions/5672308/avoid-circular-references-with-services-and-di

http://stackoverflow.com/questions/2053044/can-dependency-injection-prevent-a-circular-dependency

http://stackoverflow.com/questions/2608875/bad-design-constructor-of-composition-uses-this

http://www.potstuck.com/2009/01/08/php-dependency-injection/

Single Responsibility Principle
http://en.wikipedia.org/wiki/Single_responsibility_principle


Tuesday, March 25, 2014

Python: Decorator


Decorators I: Introduction to Python Decorators
http://www.artima.com/weblogs/viewpost.jsp?thread=240808

Decorators II: Introduction to Python Decorators
http://www.artima.com/weblogs/viewpost.jsp?thread=240845

Tuesday, July 2, 2013

C/C++: C++ start program

http://www.cplusplus.com/doc/tutorial/program_structure/

Structure of a program

Probably the best way to start learning a programming language is by writing a program. Therefore, here is our first program: 

1
2
3
4
5
6
7
8
9
10
// my first program in C++

#include <iostream>
using namespace std;

int main ()
{
  cout << "Hello World!";
  return 0;
}
Hello World!


The first panel (in light blue) shows the source code for our first program. The second one (in light gray) shows the result of the program once compiled and executed. To the left, the grey numbers represent the line numbers - these are not part of the program, and are shown here merely for informational purposes.

The way to edit and compile a program depends on the compiler you are using. Depending on whether it has a Development Interface or not and on its version. Consult the compilers section and the manual or help included with your compiler if you have doubts on how to compile a C++ console program.

The previous program is the typical program that programmer apprentices write for the first time, and its result is the printing on screen of the "Hello World!" sentence. It is one of the simplest programs that can be written in C++, but it already contains the fundamental components that every C++ program has. We are going to look line by line at the code we have just written:

// my first program in C++
This is a comment line. All lines beginning with two slash signs (//) are considered comments and do not have any effect on the behavior of the program. The programmer can use them to include short explanations or observations within the source code itself. In this case, the line is a brief description of what our program is. 
#include <iostream>
Lines beginning with a hash sign (#) are directives for the preprocessor. They are not regular code lines with expressions but indications for the compiler's preprocessor. In this case the directive #include <iostream> tells the preprocessor to include the iostream standard file. This specific file (iostream) includes the declarations of the basic standard input-output library in C++, and it is included because its functionality is going to be used later in the program. 
using namespace std;
All the elements of the standard C++ library are declared within what is called a namespace, the namespace with the name std. So in order to access its functionality we declare with this expression that we will be using these entities. This line is very frequent in C++ programs that use the standard library, and in fact it will be included in most of the source codes included in these tutorials.
int main ()
This line corresponds to the beginning of the definition of the main function. The main function is the point by where all C++ programs start their execution, independently of its location within the source code. It does not matter whether there are other functions with other names defined before or after it - the instructions contained within this function's definition will always be the first ones to be executed in any C++ program. For that same reason, it is essential that all C++ programs have a main function.

The word main is followed in the code by a pair of parentheses (()). That is because it is a function declaration: In C++, what differentiates a function declaration from other types of expressions are these parentheses that follow its name. Optionally, these parentheses may enclose a list of parameters within them.

Right after these parentheses we can find the body of the main function enclosed in braces ({}). What is contained within these braces is what the function does when it is executed.
cout << "Hello World!";
This line is a C++ statement. A statement is a simple or compound expression that can actually produce some effect. In fact, this statement performs the only action that generates a visible effect in our first program.

cout is the name of the standard output stream in C++, and the meaning of the entire statement is to insert a sequence of characters (in this case the Hello World sequence of characters) into the standard output stream (cout, which usually corresponds to the screen).

cout is declared in the iostream standard file within the std namespace, so that's why we needed to include that specific file and to declare that we were going to use this specific namespace earlier in our code.

Notice that the statement ends with a semicolon character (;). This character is used to mark the end of the statement and in fact it must be included at the end of all expression statements in all C++ programs (one of the most common syntax errors is indeed to forget to include some semicolon after a statement).
return 0;
The return statement causes the main function to finish. return may be followed by a return code (in our example is followed by the return code with a value of zero). A return code of 0 for the main function is generally interpreted as the program worked as expected without any errors during its execution. This is the most usual way to end a C++ console program.

You may have noticed that not all the lines of this program perform actions when the code is executed. There were lines containing only comments (those beginning by //). There were lines with directives for the compiler's preprocessor (those beginning by #). Then there were lines that began the declaration of a function (in this case, the main function) and, finally lines with statements (like the insertion into cout), which were all included within the block delimited by the braces ({}) of the main function.

The program has been structured in different lines in order to be more readable, but in C++, we do not have strict rules on how to separate instructions in different lines. For example, instead of 

1
2
3
4
5
int main ()
{
  cout << " Hello World!";
  return 0;
}


We could have written:

 
int main () { cout << "Hello World!"; return 0; }


All in just one line and this would have had exactly the same meaning as the previous code.

In C++, the separation between statements is specified with an ending semicolon (;) at the end of each one, so the separation in different code lines does not matter at all for this purpose. We can write many statements per line or write a single statement that takes many code lines. The division of code in different lines serves only to make it more legible and schematic for the humans that may read it.

Let us add an additional instruction to our first program:

1
2
3
4
5
6
7
8
9
10
11
12
// my second program in C++

#include <iostream>

using namespace std;

int main ()
{
  cout << "Hello World! ";
  cout << "I'm a C++ program";
  return 0;
}
Hello World! I'm a C++ program


In this case, we performed two insertions into cout in two different statements. Once again, the separation in different lines of code has been done just to give greater readability to the program, since main could have been perfectly valid defined this way:

 
int main () { cout << " Hello World! "; cout << " I'm a C++ program "; return 0; } 


We were also free to divide the code into more lines if we considered it more convenient: 

1
2
3
4
5
6
7
8
int main ()
{
  cout <<
    "Hello World!";
  cout
    << "I'm a C++ program";
  return 0;
}


And the result would again have been exactly the same as in the previous examples.

Preprocessor directives (those that begin by #) are out of this general rule since they are not statements. They are lines read and processed by the preprocessor and do not produce any code by themselves. Preprocessor directives must be specified in their own line and do not have to end with a semicolon (;).

Comments


Comments are parts of the source code disregarded by the compiler. They simply do nothing. Their purpose is only to allow the programmer to insert notes or descriptions embedded within the source code. 

C++ supports two ways to insert comments: 

1
2
// line comment
/* block comment */ 


The first of them, known as line comment, discards everything from where the pair of slash signs (//) is found up to the end of that same line. The second one, known as block comment, discards everything between the /* characters and the first appearance of the */ characters, with the possibility of including more than one line.
We are going to add comments to our second program: 

1
2
3
4
5
6
7
8
9
10
11
12
/* my second program in C++
   with more comments */

#include <iostream>
using namespace std;

int main ()
{
  cout << "Hello World! ";     // prints Hello World!
  cout << "I'm a C++ program"; // prints I'm a C++ program
  return 0;
}
Hello World! I'm a C++ program


If you include comments within the source code of your programs without using the comment characters combinations///* or */, the compiler will take them as if they were C++ expressions, most likely causing one or several error messages when you compile it.

C/C++: C 전처리기(PREPROCESSOR) 지시자(DIRECTIVES)

This article from http://cafe.naver.com/hardandsoft/52  

C 전처리기(PREPROCESSOR) 지시자(DIRECTIVES)에 관한 내용을 찾다가 괜찮은 내용이 있어서 정리합니다.
아래의 내용은 WIKIPEDIA(http://en.wikipedia.org/wiki/C_preprocessor)에서 설명되어 있는 사항들을 중점적으로
하였으며 더 자세한 내용을 알고자 하시는 분은 WIKIPEDIA를 읽어보시기 바랍니다.



C 프로그램을 컴파일(COMPILE)할 때 다음과 같은 여러 단계를 거치게 됩니다.
    - 전처리기(PREPROCESSOR) : C 파일(*.c)
    - C 컴파일러(COMPILER) : 전처리된 C 파일
    - 링커(LINKER) : 목적(OBJECT) 프로그램(*.obj)
    - 실행 파일(*.exe)

전처리기는 텍스트를 텍스트로 변환하는 순수한 텍스트 처리기로 C 프로그램을 받아 C 프로그램을 결과로 만듭니다.
전처리기는 전처리기 지시자(DIRECTIVES)라고 불리는 프로그래밍 명령어들을 처리하는 역할을 하며
또한 주석(COMMENT)을 지우는 역할을 합니다.
따라서 C 컴파일러에는 전처리기를 거친 순수한 C 프로그램이 전달되게 됩니다.
참고로 많이 사용하고 있는 용어인 프리프로세서문(PREPROCESSOR STATEMENT)는
전처리기 지시자를 사용한 문장을 의미합니다.
전처리기 지시자는 반드시 '#'으로 시작하도록 되어 있습니다.

지금부터는 다양한 종류의 전처리기 지시자들을 알아보도록 하겠습니다.

1. 소스 파일을 포함하기(Source File Inclusion)

가장 많이 사용하는 "#include" 지시자에 관한 내용입니다.

아래의 간단한 [예제 1]를 이용하여 "#include" 지시자에 대해서 설명을 하겠습니다.
[예제 1]
#include <stdio.h>
int main(void)
{
    printf("Hello, world!\n");
    return 0;
}
[예제 1]에서
전처리기는 "#include" 지시자를 만났을 떄
"#include <stdio.h>" 문장을 stdio.h의 이름을 가지는 시스템 헤더 파일로 바꾸어 버립니다.
더 정확히 말하자면,
전처리기는 stdio.h 파일의 전체 내용을 "#include <stdio.h>" 문장이 있는 위치에 옮겨 버립니다.
참고로 stdio.h 파일에는 printf() 함수가 정의되어 있습니다.

전처리기 지시자 "#include"는 다음과 같은 두 가지 형태로 사용합니다.
    (1) #include <파일 이름>
    (2) #include "파일 이름"

(1)의 경우는 C에서 미리 정의된 디렉토리(Standard Directory Include Path)에서 파일을 찾게 되고
(2)의 경우는 현재의 디렉토리(Current Source Directory)에서 파일을 찾게 됩니다.
일반적으로 (2)의 경우는 프로그래머가 파일을 생성하는 경우에 해당하겠지요.
찾고자 하는 파일이 없다면, 두 경우 모두 에러가 발생합니다.

일반적으로 <파일 이름> 또는 "파일 이름"의 파일은 *.h의 확장자를 사용하지만 다른 확장자를 사용하는 것도 가능합니다.
확장자에 대한 제약이 없습니다.

프로그래머가 프로그래밍을 하다보면 "#include"가 중복(Double Inclusion)되는 경우가 종종 발생합니다.
[예제 2]
grandfather.h
struct foo {
    int member;
};
father.h
#include "grandfather.h"
child.c
#include "grandfather.h"
#include "father.h"
[예제 2]의 경우
"child.c"를 컴파일을 하면 "foo"라는 타입이 두 번 정의(Double Inclusion)가 되어서 컴파일 에러가 발생합니다.
위와 같은 상황을 방지하기 위해 "grandfather.h"를 [예제 3]과 같이 변경을 합니다.
[예제 3]
grandfather.h
#ifndef GRANDFATHER_H
#define GRANDFATHER_H
struct foo {
    int member;
};
#endif

또는

#pragma once
struct foo {
    int member;
};
[예제 3]에서 새로운 전처리기 지시자들인 "#ifndef", "#define", "#endif", "#pragma" 등은 아래에서 설명할 예정입니다.

2. 조건부 컴파일(Conditional Compilation)

조건부 컴파일을 위해서 사용하는 전처리기 지시자들에는 "#if", "#ifdef", #ifndef", "#else", "#elif", "#endif" 등이
있는데 이들은 단독으로 사용이 불가능합니다.
    - "#if" 지시자는 "#endif" 지시자와 함께 사용해야 합니다.
    - "#elif", "#else" 지시자는 "#if" 지시자와 함께 사용할 때만 사용 가능합니다.
    - "#ifdef", "#ifndef" 지시자는 "#endif" 지시자와 함께 사용해야 합니다.

위의 전처리기 지시자들은 보기만 해도 쉽게 이해가 가능할 것으로 보이기 때문에 간단한 설명으로 끝내고자 합니다.

먼저 "#if", "#elif", "#else", "#endif" 등의 전처리기 지시자에 대한 설명을 하겠습니다.
[예제 4]
#if (EX_MACRO > 2)    /* EX_MACRO는 "#define" 전처리기 지시자에 의해 정의가 되어 있어야 합니다. */
    A1    /* 문장 또는 문장들 */
#elif (EX_MACRO == 1)    /* elif : else if */
    A2    /* 문장 또는 문장들 */
#else
    A3    /* 문장 또는 문장들 */
#endif
[예제 4]에 대해 간단한 설명을 하겠습니다.
[예제 4]는 "#if" 지시자 다음에 있는 조건(예제의 경우 "(EX_MACRO > 2)")이 참이라면 A1의 내용을 C 컴파일러에게
전달하고 거짓이라면 "#elif" 지시자 다음에 있는 조건(예제의 경우 "(EX_MACRO == 1)")을 체크하여 참이라면
A2의 내용을 C 컴파일러에게 전달하고 거짓이라면 "#else" 지시자의 A3 내용을 C 컴파일러에게 전달합니다.
위 예제에서 더 많은 조건들을 사용하고자 한다면 "#elif" 지시자를 여러 번 사용하면 되고,
간단한 조건을 사용하고자 한다면 "#elif", "#else" 지시자들을 하나 또는 모두 빼면 됩니다.

"#if", "#elif" 지시자에서 사용하는 조건은 아래와 같은 형태를 가질 수 없음에 주의해야 합니다.
[예제 4]
#if (EX_MACRO > 2.0)    /* 실수를 이용한 조건은 안됩니다. */
    A1    /* 문장 또는 문장들 */
#endif
#if (EX_MACRO == "OK")    /* 문자열(STRING)을 이용한 조건은 안됩니다. */
    A2    /* 문장 또는 문장들 */
#endif
아래에는 "#ifdef", "#ifndef" 등의 전처리기 지시자에 대한 설명을 하겠습니다.
[예제 6]
#ifdef EX_MACRO    /* EX_MACRO가 "#define" 전처리기 지시자에 의해 정의가 되어 있는지 ? */
    B1    /* 문장 또는 문장들 */
#endif
#ifndef EX_MACRO    /* EX_MACRO가 "#define" 전처리기 지시자에 의해 정의가 되어 있지 않은지 ? */
    B2    /* 문장 또는 문장들 */
#endif
[예제 6]에서는 "#ifdef" 지시자 다음의 매크로(MACRO, 예제의 경우 "EX_MACRO)가 정의되어 있으면
B1의 내용을 C 컴파일러에게 전달합니다. 그리고 "#ifndef" 지사자 다음의 매크로(MACRO, 예제의 겨우 "EX_MACRO)가
정의되어 있지 않으면 B2의 내용을 C 컴파일러에게 전달합니다.
여기서 매크로(MACRO)는 "#define" 전처리기 지시자를 이용하여 정의를 하는 것입니다.

3. 매크로 정의(MACRO DEFINITION)

매크로 정의는 매개변수(PARAMETER)의 유무에 따라서 다음과 같은 두 가지 형태를 가지고 있습니다.
    (1) #define 매크로_이름 매크로_의미
        : Object-like MACRO
    (2) #define 매크로_이름(매개변수) 매크로_의미
        : Function-like MACRO
        : 일반적으로 매크로 함수라는 용어를 사용합니다.
        : 이와 같은 형태의 매크로 정의를 사용할 때 주의할 점이 있습니다.
          매크로_이름과 '(' 는 반드시 붙여서 사용해야 합니다.
          매크로_이름과 '(' 사이에 여백이 있다면 (2)의 매크로 정의는 (1)의 매크로 정의과 같이 취급을 합니다.

전처리기는 소스 파일에서 "#define" 지시자에 의해 정의된 매크로_이름이 나타날 때마다
매크로_이름의 위치에 매크로_의미에 해당하는 내용을 그대로 바꾸어서
이 결과을 C 컴파일러에게 전달합니다.
참고로 매크로_이름에 해당하는 매크로_의미가 없다면 기술하지 않아도 상관이 없습니다.

몇 가지 예제를 이용하여 매크로 정의 및 주의할 점에 대해서 이해해 보겠습니다.
[예제 7] Object-like MACRO
#define PI 3.141592
...
    area = PI * r * r;    /* 원의 넓이 */
...
원의 넓이를 구하는 식인 [예제 7]에서
전처리기는 PI(매크로_이름)의 위치에 3.141592(매크로_의미)의 내용을 [예제 8]과 같이 바꾸어서 C 컴파일러에게
전달합니다. 따라서 C 컴파일러는 PI라는 값에 대해서 알지 못하게 됩니다.
[예제 8]
...
    area = 3.141592 * r * r;
...
라디안(RADIAN) 값을 각도(DEGREE) 값으로 변경하는 매크로 함수의 예제([예제 9])를 보겠습니다.
[예제 9] Function-like MACRO
#deifne RADTODEG(x) (x * 57.259791)
...
    deg = RADTODEG(rad);    /* RADIAN -> DEGREE */
...
전차리기가 [예제 7]을 [예제 8]로 바꾸는 것과 마찬가지로
전처리기는 [예제 9]를 [예제 10]으로 바꾸어서 C 컴파일러에게 전달합니다.
마찬가지로 C 컴파일러는 RADTODEG(x)란 내용을 알지 못하게 됩니다.
[예제 10]
...
    deg = (rad * 57.259791);
...
매크로 함수에 대해서 주의해야 할 내용을 하나 살펴 보겠습니다.

[예제 9]에서 "deg = RADTODEG(rad)" 문장을 "deg = RADTODEG(rad_1 + rad_2)" 문장의 형태로 사용했을
어떤 결과가 나타날까요?

위에서 설명하기를 전처리기는 "#define" 지시자에 의해 정의된 매크로_이름이 나타날 때마다
매크로_이름의 위치에 매크로_의미에 해당하는 내용을 그대로 바꾼다고 했습니다.

설명에 따라서 전처리기는 "deg = RADTODEG(rad_1 + rad_2)" 문장은 다음과 같이 바꿀 것입니다.
    "deg = (rad_1 + rad_2 * 57.259791)"
위 문장은 "rad_2 * 57.259791"을 먼저 계산한 후 그 결과를 "rad_1"의 값에 더해서 "deg"에 저장합니다.
이 내용은 C 문법 중 우선순위에 관한 내용입니다.
프로그래머가 원하는 결과가 위의 결과였을까요?
프로그래머가 "rad_1 + rad_2"를 먼저 계산한 후 그 결과를 "57.259791"에 곱한 결과를 원했다면
프로그래머의 의도와는 전혀 다른 결과가 나타나게 될 것입니다.
위의 생각이 일반적일 것이라고 봅니다(프로그래머의 의도에 따라 다를 수도 있습니다).
그러면 어떻게 해야할 것일까요?
[예제 11]
#deifne RADTODEG(x) ((x) * 57.259791)
...
    deg = RADTODEG(rad_1 + rad_2);
...
[예제 9]의 "#define" 지시자가 있는 문장을 [예제 11]과 같은 문장으로 변경한다면 프로그래머의 의도대로 될 것입니다.
간단한 내용이지만 주의해야 할 사항입니다.

다음은 매크로_의미가 여러 줄을 이용하여 표현될 경우를 보겠습니다.
위와 같은 상황이 발생했을 경우
각 줄의 끝에 '\'를 추가하여 매크로_의미가 여러 줄을 가진다는 것을 전처리기에 알려줍니다.
[예제 12]는 한 줄로도 가능한 매크로 정의를 여러 줄을 이용하여 표현할 수 있음을 보여줍니다.
[예제 12]
#define MIN(a, b) ((a) > (b) ? (b) : (a))
위의 정의를
#define MIN(a, b) \
((a) > (b) ? \
(b) : (a))
와 같이 표현할 수 있습니다.
마지막으로 C에서 미리 정의된 매크로를 몇 개 설명하고 매크로 정의에 대한 설명을 마치도록 하겠습니다.

프로그램들을 보다가 다음과 같은 매크로들을 여러 번 보았을 것입니다.
    (1) __FILE__ : 파일 이름, 문자 스트링(STRING)
    (2) __LINE__ : 파일의 현재 줄 번호, 문자 정수(INTEGER)
    (3) __DATE__ : 컴파일할 때의 날짜, 문자 스트링(STRING)
    (4) __TIME__ : 컴파일할 때의 시간, 문자 스트링(STRING)
[예제 13]
...
fprinft(stderr, "[FILE %s, LINE %d]\n", __FILE__, __LINE__);
...
fprinft(stderr, "[DATE %s, TIME %s]\n", __DATE__, __TIME__);
그 외에도 C에서 미리 정의된 매크로들이 많이 있습니다.

4. 전처리문 지시자에 사용할 수 있는 연산자들

전처리문 지시자에 사용할 수 있는 연산자들에는 다음과 같은 것들이 있습니다.
    - # : STRINGIZING OPERATOR
    - ## : TOKEN CONCATENATION
    - defined

'#' 연산자는 바로 뒤의 인자를 스트링으로 바뀌어 주는 역할을 합니다.
[예제 14]
#define QUOTEME(x) #x
...
fprintf(stderr, "%s\n", QUOTEME(1+2));
...
의 경우 "QUOTEME(1+2)"가 있는 문장은 "1+2"가 스트링으로 변경되어 다음과 같습니다.
fprint(stderr, "%s\n", "1+2");
'##' 연산자는 'x##y'의 형태로 사용하는데 x와 y를 붙여 하나의 이름으로 만듭니다.
[예제 15]
#define CON(x, y) (x##y)
...
CON(i, 1) = 100;
...
의 경우 "CON(i, 1)"이 있는 문장은 다음과 같이 바뀝니다.
i1 = 100;
[예제 16]
#define MYCASE(item, id) \
case id: \
    item##_##id = id; \
break
...
switch (x) {
    MYCASE(widget, 23)
}
...
의 경우 "MYCASE(widget, 23)" 문장은 다음과 같이 바뀝니다.
case 23:
    widget_23 = 23;
break;
[예제 17] 아래의 예제를 테스트 해 보시기 바랍니다.
enum {
    OlderSmall = 0,
    NewerLarge = 1
};
#define Older Newer
#define Small Large
#define _replace_1(Older, Small) Older##Small
#define _replace_2(Older, Small) _replace_1(Older, Small)
...
void printout(void)
{
    /* _replace_1(Older, Small) becomes OlderSmall (not NewerLarge),
        despite the #define calls above. */
    printf("Check 1: %d\n", _replace_1(Older, Small));
    /* The parameters to _replace_2 are substituted before the call
        to _replace_1, so we get NewerLarge. */
    printf("Check 2: %d\n", _replace_2(Older, Small));
}
...
위 예제의 결과는 다음과 같습니다.
Check 1: 0
Check 2: 1
마지막으로 "defined" 연산자는 "#if" 문에서 사용하는 것으로 [예제 18]과 같이 사용합니다.
[예제 18]
#if defined(EX_MACRO)    /* EX_MACRO가 "#define" 전처리기 지시자에 의해 정의가 되어 있는지 ? */
    A    /* 문장 또는 문장들 */
#endif
이것은 연산자 이름을 보면 알 수 있듯이
"defined" 연산자 다음의 매크로(예제의 경우 "EX_MACRO)가 정의되어 있으면
A의 내용을 C 컴파일러에게 전달합니다.
[예제 18]의 경우는 "#ifdef" 지사자와 동일한 기능을 합니다.
"#ifndef" 지시자와 비슷한 기능을 하는 것이 "!defined"의 형태입니다(여기서 '!'는 "NOT"을 의미합니다).
그러나 여러 개의 매크로들이 정의되어 있는지를 판단하고자 할 때에는 [예제 19]의 경우와 같이
"defined" 연산자를 사용하는 편리한 경우가 많습니다.
[예제 18]
/* EX_MACRO_1, EX_MACRO_2가 "#define" 전처리기 지시자에 의해 정의가 되어 있고
    EX_MACRO_3가 "#define" 전처리기 지시자에 의해 정의가 되어 있지 않은지 ? */
#if defined(EX_MACRO_1) && defined(EX_MACRO_2) && !defined(EX_MACRO_3)
    A    /* 문장 또는 문장들 */
#endif
5. 사용자 정의의 컴파일 에러(ERROR) 및 경고(WARNING) 메시지

"#error" 지시자는 전처리기에게 에러 메시지를 출력시키고 컴파일 작업을 하지 않도록 하는 것입니다.
[예제 19]
#ifdef WINDOWS
    ...    /* 윈도우즈 관련 코드 */
#elif defined(UNIX)
    ...    /* 유닉스 관련 코드 */
#else
    #error "What's your operating system?"
#endif
[예제 19]는 "WINDOWS" 및 "UNIX"란 매크로가 정의되어 있지 않으면 "What's your operating system?"란 에러 메시지를
출력시키고 컴파일 작업을 멈춥니다.

"#warning" 지시자는 전처리기에게 경고 메시지를 출력시키고
"#error" 지시자와는 다르게 컴파일 작업을 계속 하도록 하는 것입니다.
[예제 20]
#ifdef ABC
    ...    /* 문장 또는 문장들 */
#else
    #warning "Do not use ABC, which is deprecated. Use XYZ instead."
#endif
[예제 20]은 "ABC"란 매크로가 정의되어 있지 않으면 "Do not use ABC, which is deprecated. Use XYZ instead."란
경고 메시지를 출력시키고 컴파일 작업은 계속 진행합니다.
6. 그 외의 전처리문 지시자들

    - #undef
    - #inline
    - #pragma

"#undef" 지시자는 "#define" 지시자와는 정반대의 역할을 하는 것으로 이미 정의된 매크로를 취소시켜서
정의되지 않은 상태로 만들고자 할 때 사용합니다.
[예제 21]
#define SIZE 100
i = SIZE;
#undef SIZE
i = SIZE;
[예제 21]에서 처음의 "i = SIZE;"를 수행할 때에는 SIZE가 100으로 정의가 되어 있으므로 "i = 100;"과 같지만
두 번쩨의 "i = SIZE"는 "#undef SIZE"에 의해 SIZE의 정의가 취소되었기 때문에 에러가 발생합니다.
"#undef" 지시자를 사용하면 프로그램의 부분에 따라서 같은 이름의 매크로를 다른 의미로 지정하여
사용할 수도 있습니다([예제 22]).
[예제 22]
#define SIZE 100
i = SIZE;
#undef SIZE
#define SIZE 200
i = SIZE;
만약 [예제 22]에사 "#undef SIZE"를 사용하지 않고 [예제 23]과 같이 사용하게 되면
SIZE를 중복해서 정의한 것이 되므로 컴파일할 때 에러가 발생합니다.
[예제 23]
#define SIZE 100
i = SIZE;
#define SIZE 200
i = SIZE;
"#line" 지시자는 사용자를 위한 문장이기 보다는 컴파일러 자체를 위한 지시자입니다.
[예제 24] "test.c"
#include <stdio.h>                        /* test.c의   1번 라인 */
int main(void)                            /* test.c의   2번 라인 */
{                                         /* test.c의   3번 라인 */
    int x, y;                             /* test.c의   4번 라인 */
    x = 1;                                /* test.c의   5번 라인 */
    #line 100 "main.c"                    /* main.c의 100번 라인 */
    y = 1;                                /* main.c의 101번 라인 */
    fprintf(stderr, "%d, %d\n", x, y);    /* main.c의 102번 라인 */
}                                         /* main.c의 102번 라인 */
[예제 24]는 "test.c"라는 파일에 대해서 "#line" 지시자를 사용한 예를 보여줍니다.
처음 5줄을 처리할 때는 파일의 이름이 "test.c"가 되고 라인 번호도 차례대로 1, 2, ... 5가 됩니다.
그런데 "line 100 "main.c""를 만나면서 파일의 이름은 "main.c"로 바뀌고 라인 번호도 6이 아닌 100부터 시작하게 됩니다.

마지막으로 "#pragma" 지시자는 컴파일러에 따라 다를 수 있기 때문에
"#pragma" 지시자에 대해서는 컴파일러에 대해 설명해 놓은 문서를 참고하기기 바랍니다.



지금까지 C 전처리기(PREPOCESSOR) 지시자(DIRECTIVES)에 대해서 설명하였습니다.
저도 C에 대해서는 초보자 수준이기 때문에 제가 공부한다는 생각으로 정리를 하였습니다.
모자란 부분이 많겠지만
이 글을 읽으시는 분들께 도움이 되었으면 합니다.
C에 대해 고수분들 또는 어떤 분들께서라도 잘못된 내용들이 있거나
도움을 주실 내용들이 있다면 따가운 충고 부탁드리겠습니다.