본문 바로가기

awesome-c Beginner 번역/Building C Projects

<비공식 번역>awesome-c Beginner 번역 : 6. Preprocessing

현재위치


1. Configuration

2. Standard directory dectection

3. Source file dependency calculation

4. Header file location

5. Header precompileation

6. Preprocessing

7. Compliation and assembly

8. Object file dependency calculation

9. Linking

10. Installation

11. Resource linking

12. Package generation

13. Dynamic linking

<주의!!>

nethack4.org의 'Building C Projects'의 공식적인 번역이 아니며 수를 받은 것 역시 아닙니다!!




6: Preprocessing (전처리)


이 포스트를 통해 예를 들어보면, C로 만든 Hello World 프로그램을 사용하겠습니다.


#include <stdio.h>


int main(void)

{

fputs("Hello, world!\n", stdout);

return 0;

}


이제, 저는 #include <stdio.h> 줄에 포커스를 맞추고 싶습니다. 보통 사람들이 생각하는 오해는 C에서 라이브러리를 쓸때 일반적으로 fputs의 정의가 어떻게든 "연결" 된다고 생각하는 것입니다. 실제로의 영향은 정말 다릅니다: 컴파일러에게 fputs의 타입stdout(표출출력)을 말합니다. 리눅스 상에서 위 프로그램은 다음 이것과 100% 동일합니다:


struct foo;

typedef struct foo FILE;


extern int fputs(const char *, FILE *);

extern FIL *stdout;


int main(void)

{

fputs("Hello, world!\n", stdout);

return 0;

}


여기에는 stdio.h에 대한 어떠한 언급도 없고, 여전히 이 프로그램은 작동합니다. 분명히, 그것은 더이상 연결과는 아무런 상관이 없습니다.


실제로 여기에서 stdio.h에 역할은 타입, 함수 그리고 변수, 몇몇 매크로 정의를 선언한 목록입니다. 표준 C에서는 컴파일러가 이러한 정의를 구현하는 방법에 제한을 두지 않지만, 실제로는 stdio.h같은 헤더는 거의 항상 C의 통용어구로서 구현됩니다. (원래 프로그램에서 배열의 길이에 따른 buffer overflow 체크 코드 같은 C로 직접적인 표현을 할 수 없는 일들을 컴파일러 관련 확장명을 사용합니다.)


이번 경우에서, 우리가 정의한 FILE은 불완전한 타입입니다. (컴파일러는 구조체의 일종인 FILE의 정의를 프로그램을 동작하기위해 알필요는 없습니다. 왜냐면 모든 멀쩡한 컴파일러들은 동일하게 모든 구조체에 대한 포인터로 취급하기 때문입니다.); const char *FILE *인자로 int * 형 함수인 fputs 그리고 FILE 타입의 변수인 stdout입니다. extern의 의미는 오직 선언입니다. 그리고 이 파일은 해당 문제의 함수와 변수의 정의를 반드시 포함하지 않습니다. (링커가 포함될 때, 나중에 정의를 실제로 볼 수 있습니다.)


리눅스에서 stdout은 사실 변수입니다. 그러나 윈도우즈에서는 mingw를 사용합니다. 접근 기능은 표준 파일핸들의 stdin, stdout 그리고 stderr같은 값을 찾는 것이 필요합니다. 그래서 전처리기(preprocessor)는 다음과 같이 프로그램을 확장합니다. (몇 세부적인 내용과 관계없는 수천줄은 생략했습니다.):


struct foo;

typedef struct foo FILE;


extern int fputs(const char *, FILE *);

extern FILE *__iob_func();


int main(void)

{

fputs("Hello, world\n", &__iob_func()[1]);

return 0;

}


이제 우리는 stdio.h같은 표준 헤더 파일의 목표가 무언지를 볼 수 있습니다: 이것은 종이(번역자 : 아마도 정의문이라고 생각됩니다)를 넘어 표준 라이브러리의 세부구현에 따른 시스템별 차이입니다. 그렇다면 전처리기가 수행하는 것은 #include(헤더 파일의 정의를 사용하기 위해) 또는 #define(&__iob_func()[1]stdout으로 소스파일에서 심볼을 치환하는 일)같은 "전처리기 지시문"을 평가합니다. 그 결과는 여러분이 직접 작성하는 C 소스파일입니다. 그러나 조금 더 시스템적 관점이기보단 여러분 프로젝트의 소스 파일에 해당될 것입니다.


게다가 stdio.h같은 표준 헤더파일에서 프로젝트는 또한 그들만의 헤더파일을 가질 수 있습니다.(예를 들어 NetHack의 hack.h) 전처리기의 시점의 핵심으로부터 이러한 작업은 표준 헤더파일과 정확히 같은 작업이며 그 내용도 표준헤더와 유사합니다.(비록 명백히 시스템 특성이 덜한 경향이 있습니다.) 이들은 <>보다는 ""을 사용함으로 구별됩니다. 즉 #include "hack.h", #include <stdio.h>처럼 쓰는거죠.


또한 전처리기는 전통적으로 소스에서 주석을 제거하는 책임이 있습니다. 이전 버전과의 호환성을 위해 여전히 기본적으로 작동합니다. 그러나 현대의 컴파일러는 주석을 큰 문제거리로 삼지 않고 전처리기가 주석을 그대로 남겨둔다 하더라도 빌드 작업을 원활히 진행될 것입니다.


오늘날의 전처리기는 거의 항상 컴파일러에 내장되어 있습니다. 거의 밀접하게 작용하기 때문입니다. 그러나 전처리기는 어디까지나 개별적으로 동작합니다. 전처리기를 동작시키는 명령에 대한 표준 이름은 cpp입니다.(저는 "C Preprocessor"라고 생각합니다.); 이것은 보통 전처리기 수행을 명시하는 E 명령(command)라인 옵션을 제공하는 C 컴파일러의 wrapper로 구현합니다.(그리고 사실 aimake는 컴파일러와 전처리기간의 불일치를 방지하기위해 개별적인 전처리기 작업에서 E 명령(command)를 사용합니다.) 전처리기 출력을 위한 표준 파일이름은 .i 입니다.



출처1 : https://github.com/aleksandar-todorovic/awesome-c

출처2 : http://nethack4.org/blog/building-c.html