[C언어 강좌] #19-2 전처리기와 분할 컴파일


조건부 컴파일

C언어는 다양한 운영체제에서 사용되었기 때문에 각 운영체제가 제공하는 표준 함수나 동작, 라이브러리가 조금씩 다를 수 있습니다. 같은 운영체제를 사용한다고 하더라도 사용하는 컴파일러나 라이브러리에 따라서 일부 함수가 없는 경우가 있을 수도 있구요.

 

예를 들여서 main.c라는 파일을 만들어서 C로 프로그램을 하나 만들었는데 이게 윈도우에서는 제대로 동작하는데,

리눅스에선 함수들 몇개가 제대로 동작하지 않아서 프로그램이 정상적으로 동작하지 않는 경우를 생각해봅시다.

 

지금까지 배워본 바로는 별다른 해결방법이 없어서 윈도우 용은 main_window.c 로 만들고 리눅스 용은 main_linux.c 로 파일을 만들어서 해결을 했다고 봅시다.

 

분명 둘다 기능은 동일하게 추가해야 하는데 함수 몇개때문에 파일이 분할되서 프로그램이 커질수록 유지보수가 상당히 귀찮고, 어려워집니다. 심지어 컴파일도 따로따로 해서 배포해줘야 합니다.

 

이러한 문제점을 해결하기 위해 존재하는 것이 바로 조건부 컴파일 입니다. 전처리기가 제공하는 유용한 기능이 바로 이 기능입니다. 조건부 컴파일은 주로 매크로 상수의 존재 유무 또는 매크로 상수 값을 검사해서 수행하게 됩니다.

 

조건부 컴파일이란 조건에 따라 특정 코드를 컴파일하도록 만들 수 있는 기능입니다.

 

조건부 컴파일에 사용되는 전처리기 지시자는 #if, #elif, #else, #endif, #ifdef, #ifndef 등이 있습니다.

지시자가 상당히 많아보입니다만, 모양을 보면 뭔가 익숙해보이는것들이 굉장히 많고 벌써부터 대충 감이 잡히시는 분들도 있을겁니다.

 

#if~#endif

조건부 컴파일에 사용되는 전처리기 지시자 중에 가장 기본이 되는 것이 #if~#endif 문입니다.

 

사용은 다음과 같이 합니다. 조건에 따라 컴파일 하는데 그 조건식은 우리가 기존에 배웠던 if문(조건문) 생각하시면 되고 전처리기 지시문이기 때문에 앞에 #을 붙여줘야 합니다.

 

예를 들어서 위 코드는 CODE 라는 값이 1일때는 위 문구를 컴파일 하고 아니면 주석 처럼 무시하라는 전처리기 지시입니다.

 

그리고 기존에 조건문은 if문의 블럭을 구분할때 중괄호로 구분했지만 전처리기의 경우 그런 중괄호를 사용하지 않기 때문에 #if 로 열어줬으면 반드시 마지막은 #endif 로 닫아줘야 합니다.

 

if문을 쓸때 중괄호를 넣어줬으면 나중에 닫아줘야 하는것과 같습니다.

 

#include <stdio.h>

#define CODE 2

int main()
{
	double num1 = 0, num2 = 0, result = 0;
	
	printf("실수 두개를 입력해주세요 : ");
	scanf("%lf %lf", &num1, &num2);
	

	#if(CODE == 1)
		result = num1 + num2;
		printf("덧셈 결과 : %lf", result);
	#endif
	
	#if(CODE == 2)
		result = num1 - num2;
		printf("뺄셈 결과 : %lf", result);
	#endif
	
	#if(CODE == 3)
		result = num1 * num2;
		printf("곱셈 결과 : %lf", result);
	#endif
	
	#if(CODE == 4)
		result = num 1 / 2;
		printf("나눗셈 결과 : %lf \n", result);
	#endif
	
    return 0;
}

실수 두개를 입력해주세요 : 2 3
뺄셈 결과 : -1.000000
--------------------------------
Process exited after 2.119 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

#define CODE 2 를 통해서 매크로 상수 CODE를 2로 정의합니다.

 

전처리기는 소스코드를 읽어들이는데 

 

	#if(CODE == 1)
		result = num1 + num2;
		printf("덧셈 결과 : %lf", result);
	#endif

현재 CODE의 값은 2이므로 이렇게 조건식에 맞지 않으면 소스코드에 포함되어 있지 않은걸로 간주하고

컴파일러 입장에서는 그냥 주석처럼 무시하게 되고

 

	#if(CODE == 2)
		result = num1 - num2;
		printf("뺄셈 결과 : %lf", result);
	#endif

해당 코드의 경우엔 CODE의 값이 2인 조건이 성립(참)하므로

전처리기에 의해

 

		result = num1 - num2;
		printf("뺄셈 결과 : %lf", result);

로 바뀌게 됩니다. 소스코드에 포함되어 컴파일이 되게 되는것이죠.

 

나머지 CODE가 3인경우, 4인경우도 컴파일 할땐 주석처럼 무시되게 됩니다.

이것이 바로 조건부 컴파일. 조건에 따라 컴파일을 하거나 안하거나 할 수 있는것이죠.

 

저 위 소스코드를 작성하고 CODE값을 바꿔보면서 각각 다른 결과값이 나오는지 한번 확인해보시길 바랍니다.

 

#if ~ #else ~ #endif

생각해보니 C언어에서 조건문을 배울때 if 말고도 else도 있었고 else if 도 있었죠.

조건부 컴파일에서 조건문 else의 역할을 하는게 #else입니다.

 

#include <stdio.h>

#define CODE 2

int main()
{
	double num1 = 0, num2 = 0, result = 0;
	
	printf("실수 두개를 입력해주세요 : ");
	scanf("%lf %lf", &num1, &num2);
	

	#if(CODE == 1)
		result = num1 + num2;
		printf("덧셈 결과 : %lf", result);
	#else
		result = num1 - num2;
		printf("뺄셈 결과 : %lf", result);
	#endif
	
    return 0;
}

실수 두개를 입력해주세요 : 10 20
뺄셈 결과 : -10.000000
--------------------------------
Process exited after 0.595 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

#if ~ #else 는 다음과 같이 씁니다.

끝에 #endif 로 닫아주는건 항상 잊지 마시구요.

 

CODE가 1이면 덧셈 결과 코드를 포함시켜 컴파일하게 되고, 

만약 1이 아닌 다른 모든값이 들어오면 뺄셈결과를 컴파일 하게 됩니다.

 

그렇게 어려운 내용은 아니니 넘어가겠습니다.

 

#if ~ #elif ~ #else # endif

if문도 else문도 다봤으니 이제 else if 가 전처리기 문에선 어떤것에 해당되는지 알아봐야겠죠?

조건부 컴파일 조건식에서 C언어의 else if와 동일한게 #elif 입니다. (짧게 줄여쓴 것 입니다.)

 

#include <stdio.h>

#define CODE 4

int main()
{
	double num1 = 0, num2 = 0, result = 0;
	
	printf("실수 두개를 입력해주세요 : ");
	scanf("%lf %lf", &num1, &num2);
	

	#if(CODE == 1)
		result = num1 + num2;
		printf("덧셈 결과 : %lf", result);
	#elif(CODE == 2)
		result = num1 - num2;
		printf("뺄셈 결과 : %lf", result);
	#elif(CODE == 3)
		result = num1 - num2;
		printf("곱셈 결과 : %lf", result);
	#else
		result = num1 / num2;
		printf("나눗셈 결과 : %lf", result);
	#endif
	
    return 0;
}

실수 두개를 입력해주세요 : 20 10
나눗셈 결과 : 2.000000
--------------------------------
Process exited after 0.7451 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

이 내용도 그다지 어렵지 않을거라고 생각합니다.

조건문 else if 만 하나 더 추가한것이니깐요.

 

전처리기에 의해 조건에 따라 컴파일 하는게 핵심이라고 보시면 됩니다.

 

#ifdef ~ #endif & #ifndef ~ #endif

마지막으로 조건부 컴파일에 사용되는 전처리기 지시자 중 #ifdef~endif #ifndef~#endif 에 대해 알아보겠습니다.

 

#ifdef는 'if defined' 라는 문장을 줄여서 만든 것으로 #ifdef 지시자 뒤에 지정한 매크로 이름이 선언되어 있으면 컴파일을 수행합니다.

 

#if 지시자의 경우 매크로 상수의 조건문이 참 & 거짓을 판단하여 컴파일을 진행하는데

#ifdef는 매크로가 정의되어 있느냐만을 판단하기 때문에 매크로 상수의 값과 관계 없이 정의되어 있냐(존재 여부) 없냐가 중요합니다.

 

사용은 아래와 같이 합니다.

#include <stdio.h>

#define ADD
#define MUL

int main()
{
	double num1 = 0, num2 = 0, result = 0;
	
	printf("실수 두개를 입력해주세요 : ");
	scanf("%lf %lf", &num1, &num2);
	
	#ifdef ADD
		result = num + num2;
		printf("result : %lf\n", result);
	#endif
	
	#ifdef MUL
		result = num * num2;
		printf("result : %lf\n", result);
	#endif
	
    return 0;
}

실수 두개를 입력해주세요 :  10 20
result : 30.000000
result : 200.000000

--------------------------------
Process exited after 0.8598 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

3,4 행에서 매크로 이름 ADD와 MUL을 선언합니다. 값은 따로 정해주지 않았습니다.

 

	#ifdef ADD
		result = num + num2;
		printf("result : %lf\n", result);
	#endif
	
	#ifdef MUL
		result = num * num2;
		printf("result : %lf\n", result);
	#endif

그리고 #ifdef를 통해 매크로 상수가 정의되어 있는지 체크합니다.

정의되어 있으면 포함시켜서 컴파일 하고, 아니면 주석처럼 무시하게 됩니다.

 

정의되어 있는지만 체크하기 때문에 ADD와 MUL 값은 중요하지 않습니다.

마지막으로 #endif로 닫아주는것 잊지마세요.

 

다음으로 #ifndef ~ #endif 를 알아보겠습니다.

#ifndef는 #ifdef의 반대인데 'if not defined' 라는 문장을 줄여서 만든것으로 이것은 선언되어 있지 않은지 검사합니다.

 

#ifdef가 해당 매크로가 선언되어 있어야만 컴파일을 했다면

#ifndef는 해당 매크로가 선언되어 있지 않으면 컴파일을 합니다.

 

아래 예제를 가지고 ifndef의 의미를 확인해보겠습니다.

 

#include <stdio.h>

#ifndef ADD
	#define ADD
#endif

#ifndef MUL
	#define MUL
#endif

int main()
{
	double num1 = 0, num2 = 0, result = 0;
	
	printf("실수 두개를 입력해주세요 : ");
	scanf("%lf %lf", &num1, &num2);

	
	#ifdef ADD
		result = num1 + num2;
		printf("result : %lf\n", result);
	#endif
	
	#ifdef MUL
		result = num1 * num2;
		printf("result : %lf\n", result);
	#endif
	
    return 0;
}
#ifndef ADD
	#define ADD
#endif

#ifndef MUL
	#define MUL
#endif

상단 매크로 상수 선언부를 보시면 조금 달라진게 있는데 

그냥 #define 을 하지 않고 #ifndef 를 통해 조건을 검사하고 매크로 상수를 선언하고 있습니다.

 

위 소스코드의 의미는 ADD와 MUL이 선언되어 있지 않으면 선언하라는 의미입니다.

만약에 ADD와 MUL이 이미 선언되어 있으면 저 부분은 무시하고 넘어가겠죠.

이런식으로 중복 선언을 방지할 수도 있습니다.

 

*추가적으로 defined 연산자라는 것이 있습니다. 이 연산자는 매크로의 존재 여부를 판단하는데 사용합니다. 즉 #if defined(MACRO) 는 #ifdef MACRO와 같은 구문입니다. 그러나 이 연산자는 논리 연산자 &&와 ||를 이용하여 다른 조건들과 함께 사용할 수 있다는 차이점이 있습니다.

 

파일 분할 컴파일

강의 첫장부터 지금까지 작성한 프로그램은 전부 하나의 소스파일에서만 작업을 했었습니다.

사실 하나의 소스 파일로 프로그램을 계속 개발하는건 별로 좋은 생각이 아닙니다.

 

규모가 작을땐 간편할지 몰라도 프로그램의 규모가 점점 커지면 변수가 어디에있는지, 내가 이 함수를 어디에 뒀는지 스크롤하면서 한참 찾아내야 합니다. (그리고 찾았는데 이게 어떤식으로 동작하는지 잊어버리는 불상사도 발생합니다...)

 

그런데 주요 함수들을 따로 파일로 분리시켜놓고, 메인 코드에서 포함만 시켜주면 소스코드를 줄일 수 있고 함수 위치도 파일만 열어서 보면 간편하게 수정가능하죠.

 

그게 바로 지금까지 우리가 사용하던 전처리기 지시문인 #include 였구요!

한번 파일을 분할해서 컴파일 하는 방법을 알아봅시다.

 

파일 분할

//sub.c
int a = 6 b = 3;
//main.c
#include <stdio.h>
int main(void){
    int result = a+b; //오류발생
    printf("result : %d", result);
    return 0;
}

다음과 같이 sub.c에는 변수를 선언 및 초기화 해두고

main.c에서는 그걸 불러와서 덧셈을 수행하는 코드를 만들었다고 칩시다.

 

이 상태에서 main.c를 실행하면 당연히 오류가 발생합니다.

왜냐? main.c에는 변수 a와 b가 없기 때문이죠.

 

이 문제를 해결하는 방법은 사실 이전에 배웠는데 extern 키워드를 이용해주는 방법입니다.

 

//main.c
#include <stdio.h>

extern int a, b; //외부 변수 선언 (다른 소스 파일에 있는 전역변수를 사용한다.)

int main(void){
    int result = a+b; //오류발생
    printf("result : %d", result);
    return 0;
}

result : 9
--------------------------------
Process exited after 0.006255 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

 

int a,b를 선언해주는데 extern 키워드를 붙이면 외부에 int형 변수 a와 b가 이미 있으니 거기서 참조해라. 라는 의미가 됩니다.

 

보시면 아까와 다르게 출력값이 정상적으로 9 로 출력이 됩니다.

 

물론 Dev C++에서 이 외부 변수를 사용하려면 어디서 외부변수를 참조해야 할지 기준이 마련되어 있지 않아서 그냥 실행하면 쉽게 sub.c의 a, b 변수를 가져와 주진 않습니다.

 

지금 사진에 보시면 알겠지만 프로젝트를 생성해서 main.c와 sub.c를 묶어주고 실행해야 프로젝트 내부에서 참조하라는 의미로 이해하고 외부 변수를 찾게됩니다.

 

또 다른 예제를 살펴보겠습니다.

 

//sub.c
int add(int x, y){
	return x+y;
}
//main.c
#include <stdio.h>

extern int add(int x, int y);

int main(void){
    printf("result : %d", add(10, 20));
    return 0;
}

보시면 main.c에서 sub.c의 add() 함수를 호출하고 있습니다.

이게 바로 아까 말씀드린 파일의 분할입니다.

 

주요 함수만 저렇게 파일로 빼놓고 의미있는 이름을 지어주고, 메인 코드에서 포함한뒤 사용해주면 코드가 훨씬 간결해지고 나중에 함수만 수정할때 파일만 열어서 수정하면 되므로 훨씬 더 효율적인 코드가 됩니다.

 

물론 그냥 호출만 해선 안됩니다. 변수와 마찬가지로 extern 키워드를 활용해야 되는데요.

 

extern int add(int x, int y);

다음과 같이 add 함수를 extern 키워드를 붙여서 원형을 선언해주면

외부에 int add(int x, int y) 라는 형태를 가진 함수가 있으니 참조해라. 

라는 의미가 됩니다.

 

참고로 함수의 경우에 extern 키워드를 생략할 수 있습니다.

그리고 Dev C++에서 프로젝트를 생성하고 진행하니 함수 선언 자체는 안해도 잘 호출해서 가져오더군요.

그렇지만 일반적인 사용 방법은 저렇게 extern 키워드를 붙여주는게 일방적입니다.

 

//sub c
static int add(int x, int y){
	return x+y;
}
//main.c
#include <stdio.h>

extern int add(int x, int y);

int main(void){
    printf("result : %d", add(10, 20));
    return 0;
}

추가적으로 static 키워드를 이렇게 붙여주면 외부에서 참조가 불가능합니다.

 

전역변수의 경우 static 키워드를 붙이면 extern 으로 참조가 불가능하게 되고, 그 소스코드 내부에서만 전역변수로서 작동하고, 함수에 붙이면 역시 외부에서 참조가 불가능하게 됩니다.

 

이렇게 참조를 막고 그 소스코드 내부에서만 사용하게 만드는 이유는 시스템의 안정때문입니다.

시스템 프로그램과 관련된 매우 중요한 변수를 extern 키워드로 참조하면 문제가 발생할 수 있기 때문입니다.

 

이전에 다 다뤘던 내용이므로 간단히 다루고 넘어가겠습니다.

 

#include를 이용한 사용자 헤더 파일 만들기

지금까지는 *.c 로 끝나는 C언어 파일만을 컴파일 해서 사용했습니다. 이제부터는 확장자 h를 가진 헤더파일 *.h 에 대하여 알아보겠습니다.

 

헤더 파일은 전처리기 지시자인 #include 에 의하여 소스코드 파일 내부에 포함되게 됩니다.

강의 첫장부터 수도없이 써왔던 #include <stdio.h> 를 이용해서 stdio.h 를 소스코드 내부에 포함시키면

 

포함시킨 내용에 printf, scanf 같은 함수가 있고 사용할 수 있는 것 입니다.

 

위에서 배운 내용을 사용하면 함수, 변수 모두 extern 키워드를 사용해서 사용해볼 수 있지만

외부에서 참조해야할 함수가 100개면 일일히 함수 원형을 다 써줘야 합니다.

 

매우 귀찮은 일이 아닐 수 없죠. 귀찮은 일도 일인데 함수 return 형식이나 인자값이 바뀌면 원형 선언을 다 다시해줘야 합니다;;

 

이제 사용자 헤더 파일을 직접 만들어서 이 문제를 해결해봅시다.

#include를 사용해서 필요한 소스를 포함시킬땐 두가지 case가 있습니다.

 

1. 표준 라이브러리

#include <표준 라이브러리> 의 형태로 사용합니다

ex) #include <stdio.h>, #include <string.h>

 

헤더 파일을 < > 로 묶는 경우는 컴파일러가 제공하는 표준 라이브러리 헤더 파일이 모여 있는 경로에서

표준 라이브러리를 찾아서 소스코드에 포함시키라는 의미입니다.

물론 #include <myheader.h> 처럼 사용자 정의 헤더 파일을 인클루드 시킬수도 있는데 이러면 인클루드 경로에서 사용자 정의 헤더 파일을 찾게 됩니다.

 

간단히 말해서 < > 로 쓰면 이미 지정된 인클루드 경로(라이브러리 경로) 에서 파일을 찾아내라는 말입니다.

 

2. 사용자 정의 라이브러리

#include "사용자 정의 라이브러리" 의 형태로 사용합니다.

ex1) #include "myheader.h" : 상대 경로 (현재 소스코드와 같은 경로에서 찾아냄)

ex2) #include "C:\myheader.h" : 절대 경로 (지정된 경로로 들어가서 찾아냄)

 

헤더 파일을 " " 로 묶는 경우는 사용자가 만든 라이브러리 즉, 필요에 의해서 별도로 만든 사용자 정의 라이브러리를 찾아서 소스 코드에 인클루드 하라는 의미입니다.

 

간단히 말해서 원하는 경로에서 인클루드가 가능한 기능입니다.

지정된 경로에서 포함시키는 < > 보다 범위가 더 넓은것이라고 볼 수 있습니다.

 

//sub.py

#define PI 3.14
//r = 반지름 

//원의 들레 
double circle(int r){
	double result = 2 * PI * r;
	return result;
}

//원의 넓이 
double area(int r){
	double result = PI * r * r;
	return result;
}

우선 다음과 같이 sub.c 에 원의 둘레와 원의 넓이를 구하는 함수를 만들었습니다.

 

 

#include <stdio.h>

double circle(int r);
double area(int r);

int main(void){
    int r = 0 ;
    printf("반지름을 입력해 주세요 : ");
    scanf("%d", &r);

    printf("원의 둘레 : %lf\n", circle(r));
    printf("원의 넓이 : %lf", area(r));
    return 0;
}


반지름을 입력해 주세요 : 10
원의 둘레 : 62.800000
원의 넓이 : 314.000000
--------------------------------
Process exited after 1.231 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

그리고 아까와 같이 main 함수에서 sub의 함수를 써먹기 위해 원형 선언을 해주었습니다.

(extern 키워드는 생략가능하다고 했습니다.)

 

머.. 작동은 잘합니다 ㅎㅎ

 

근데 한가지 마음에 안드는게 있습니다.

double circle(int r);
double area(int r);

이 원형 선언.. 매번 써줘야 하는데 안쓸순 없을까요?

main.c 말고 다른 파일을 또 만들어서 저걸 호출해야 한다면 또 원형선언을 해줘야 하잖아요.

매우 귀찮습니다

 

C에서는 이런 귀찮은 작업을 해결하기 위해 헤더파일이라는 해결책을 제시해줬습니다.

(*.h로 끝나는 파일이 헤더 파일입니다.)

 

우선 프로젝트에 sub.h 라는 헤더 파일을 만들어 주었습니다.

 

그리고 헤더파일에 내용을 적는데 아까 적었던 원형 선언만 저렇게 적어줍니다.

 

double circle(int r);
double area(int r);

코드는 위와 같구요.

 

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

int main(void){
	int r = 0 ;
	printf("반지름을 입력해 주세요 : ");
    scanf("%d", &r);
    
    printf("원의 둘레 : %lf\n", circle(r));
    printf("원의 넓이 : %lf", area(r));
    return 0;
}


반지름을 입력해 주세요 : 10
원의 둘레 : 62.800000
원의 넓이 : 314.000000
--------------------------------
Process exited after 1.189 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

그리고 아까 원형선언은 지워버리고 #include "sub.h" 로 대체해줍니다.

컴파일 후 실행하면 제대로 작동을 하는걸 알 수 있습니다.

 

헤더 파일의 사용에 대해 감이 잡히시나요?

#include 는 어떤식으로 동작하는건지 알아보겠습니다.

그 원리는 정말 간단합니다.

 

#include "sub.h"

다음 문장은 전처리기 문이므로 당연히 컴파일 이전에 처리를 합니다.

 

#define 의 경우 치환의 개념으로 전처리를 했고, 조건부 컴파일의 경우 소스코드를 포함시키거나 주석처럼 무시시키는 방향으로 전처리를 했었죠.

 

#include의 경우 컴파일 이전 전처리 과정에서 sub.h 라는 파일 내용을 그대로 붙여넣기 한 상태로 바뀌어 버리고, 컴파일이 진행됩니다.

 

double circle(int r);
double area(int r);

그러니 #include 의 내용은 이렇게 바뀌어져 있겠죠.

 

그럼 정리해보겠습니다.

 

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

int main(void){
    int r = 0 ;
    printf("반지름을 입력해 주세요 : ");
    scanf("%d", &r);

    printf("원의 둘레 : %lf\n", circle(r));
    printf("원의 넓이 : %lf", area(r));
    return 0;
}

이러한 코드는 전처리기 과정에 의해

 

#include <stdio.h>

double circle(int r);
double area(int r);

int main(void){
	int r = 0 ;
	printf("반지름을 입력해 주세요 : ");
    scanf("%d", &r);
    
    printf("원의 둘레 : %lf\n", circle(r));
    printf("원의 넓이 : %lf", area(r));
    return 0;
}

컴파일 전 이렇게 바뀌게 됩니다. 그리고 컴파일이 진행되게 되겠죠.

그냥 전처리때 Ctrl C & V 해주는 녀석이 #include 라고 보시면 됩니다.

 

지금까지 지겹게 써왔던 #include <stdio.h> 의 경우에도 마찬가지죠..

stdio.h의 내용이 컴파일 전 대치되고 컴파일이 진행되겠죠.

 

/**
 * This file has no copyright assigned and is placed in the Public Domain.
 * This file is part of the mingw-w64 runtime package.
 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
 */
#ifndef _INC_STDIO
#define _INC_STDIO

#include <crtdefs.h>

#include <_mingw_print_push.h>

#pragma pack(push,_CRT_PACKING)

#ifdef __cplusplus
extern "C" {
#endif

#define BUFSIZ 512
#define _NFILE _NSTREAM_
#define _NSTREAM_ 512
#define _IOB_ENTRIES 20
#define EOF (-1)

#ifndef _FILE_DEFINED
  struct _iobuf {
    char *_ptr;
    int _cnt;
    char *_base;
    int _flag;
    int _file;
    int _charbuf;
    int _bufsiz;
    char *_tmpfname;
  };
  typedef struct _iobuf FILE;
#define _FILE_DEFINED
#endif

#ifdef _POSIX_
#define _P_tmpdir "/"
#define _wP_tmpdir L"/"
#else
#define _P_tmpdir "\\"
#define _wP_tmpdir L"\\"
#endif

#define L_tmpnam (sizeof(_P_tmpdir) + 12)

#ifdef _POSIX_
#define L_ctermid 9
#define L_cuserid 32
#endif

#define SEEK_CUR 1
#define SEEK_END 2
#define SEEK_SET 0

#define	STDIN_FILENO	0
#define	STDOUT_FILENO	1
#define	STDERR_FILENO	2

#define FILENAME_MAX 260
#define FOPEN_MAX 20
#define _SYS_OPEN 20
#define TMP_MAX 32767

#ifndef NULL
#ifdef __cplusplus
#ifndef _WIN64
#define NULL 0
#else
#define NULL 0LL
#endif  /* W64 */
#else
#define NULL ((void *)0)
#endif
#endif

#include <_mingw_off_t.h>

#ifndef _STDIO_DEFINED
#ifdef _WIN64
  _CRTIMP FILE *__cdecl __iob_func(void);
#define _iob  __iob_func()
#else
#ifdef _MSVCRT_
extern FILE _iob[];	/* A pointer to an array of FILE */
#define __iob_func()	(_iob)
#else
extern FILE (* __MINGW_IMP_SYMBOL(_iob))[];	/* A pointer to an array of FILE */
#define __iob_func()	(* __MINGW_IMP_SYMBOL(_iob))
#define _iob __iob_func()
#endif
#endif
#endif

//생략...

stdio.h 에 어떤 내용이 있냐고 하면 위와 같은 내용이 있습니다.

이건 아주 일부고 내용이 아주 많죠 ㅎㅎㅎ

우리는 지금까지 이걸 알필요 없었고 사용법만 익히면 되니깐 #include 로 포함을 해서 함수만 간단히 사용한 것이였구요.

 

헤더 파일엔 함수 원형 뿐만이 아니라 아래처럼 많은게 들어갈 수 있습니다.

*그러나 보통의 C코드를 넣는건 권장하지 않습니다!!

 

  • 전역 변수
  • 구조체, 공용체, 열거형
  • 함수의 원형
  • 일부 특정 함수
  • 매크로

 

드디어 C언어의 길고 긴 여정이 끝났습니다.

 

하고 싶은 말이 많지만 그 내용은 다음편인 후기편에서 뵙겠습니다.

 

제 강의를 봐주시고 따라와주신 여러분 모두 감사하고 수고하셨습니다.

 

COMMENT WRITE