C 언어에서의 Preprocessing 과정
관련 단어 등 이론적 설명은 여기 글 참고
https://lg960214.tistory.com/35
[C++] 프로그램 컴파일 과정 - 선행처리기(preprocessor), 컴파일러(compiler), 링커(linker)
C++ 프로그램 컴파일 과정 1. 소스 파일 작성 2. 선행처리기(preprocessor)에 의한 선행처리 3. 컴파일러(compiler)에 의한 컴파일 4. 링커(linker)에 의한 링크 1. 소스 파일 작성 소스 파일(.cpp) : C++ 문법에
lg960214.tistory.com
컴파일 과정 중 나오는 파일 살펴보기
Visual Studio로 진행
먼저 3개의 파일을 작성
// main.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "bbq.h"
int main() {
int a = abc(100);
printf("%d", a);
return 0;
}
// bbq.c
#include "bbq.h"
#define a 10
#define b 20
int abc(int i) {
return i + a + b;
}
// bbq.h
int abc(int i);
이 후, Visual Studio에서 다음 과정을 진행
[프로젝트] 에서, [프로젝트 속성] 클릭
- 파일로 전처리 : 전처리기가 소스 코드를 처리한 결과를 별도의 파일로 저장
- 전처리 줄 번호 표시 안 함 : 전처리된 소스 코드에 대한 위치 정보가 누락
위와 같이 설정하면, 빌드가 되지 않는다.
.i File 확인
[빌드] 에서 [컴파일] 을 눌러 컴파일 진행
이후 Project를 우클릭 후, [파일 탐색기에서 폴더 열기] 클릭해서, [x64] 폴더 -> [Debug] 폴더 내에 들어가면 .i 파일 확인 가능
Visual Studio로 드래그 앤 드랍해서 내용 확인
조건부 컴파일 후 .i 파일 내용 확인하기
// main.c 내용
#define KFC
#define BBQ
#ifdef KFC
#define MAC
#else
#undef BBQ
#undef MAC
#endif
#ifndef MAC
#define MOMS
#endif
#ifdef MOMS
HAHA;
#endif
int main() {
return 0;
}
조건부 컴파일 지시자 (#ifdef)가 참/거짓 판별하는 방법 : 코드 내 if 문의 조건 판별 방법과 같다.
조건부 컴파일 지시자 (#ifdef)
1. 참/거짓 판별하는 방법
- 코드 내 if 문의 조건 판별 방법과 같다.
2. #ifdef == #if defined
- 같은 의미이다.
3. 연산 기호 사용 가능
- #if defined BBQ && defined KFC
Header File & Source File
기본 용어와 규칙
1. include
#include : 파일 그대로 복사 붙여넣기
1. #include <stdio.h>
- stdio.h 파일을 가져옴
2. #include "abc.h"
- abc.h 유저 파일을 가져옴
2. C언어 규칙
맨 위부터 읽으면서, 함수를 등록해 놓는다.
오른쪽 코드에 대한 해결 방법
- 가장 윗 줄에 함수 선언 추가 : 함수 전방 선언이라고 함
함수 선언과 정의
1. 함수 선언
- 앞으로 이 함수를 쓸 예정이라고 알려주는 것 -> 컴파일 에러 방지
- 여러 번 선언 가능
2. 함수 선언 + 정의
- 앞으로 이 함수를 쓸 예정이며, 함수 정의(함수가 실행하는 코드 내용)를 나타내는 것
- 한 번만 정의해야 함
Include 활용 - .h 파일 내 코딩
다음과 같은 경우는 빌드가 가능할까?
C++은 오버로딩이 가능해서 빌드가 된다.
C는 Preprocessing은 되지만, 컴파일 시 에러가 발생한다.
해결 방법
1. 중복 include 로 인한 컴파일 에러 : Preprocessor에서 같은 함수를 가져옴
- Header Guard 사용
- 헤더 파일이 중복해서 포함되는 것을 방지하기 위한 기법
2. 상호 include 발생 가능
- Header Guard 사용
- 모든 Header File에 Header Guard를 쓰면 문제가 해결
3. include 순서로 인한 error
- 함수를 못 찾는 error는 전방 함수 선언을 추가
// 1. 컴파일러 Header Guard 이용
// Pragma 이용
#pragma once
// 2. 수동 Header Guard 이용 (GCC 표준이므로 추천)
// #define / #ifndef 이용 - pragma once와 동일한 기능
#ifndef __KFC_H__
#define __KFC_H__
#include "bbq.h"
#endif
Include 활용 - .c 파일 내 코딩
둘 중 오류가 나는 경우가 있을까?
이유
- .h 파일은 컴파일이 되지 않고, .c 파일은 컴파일을 수행한다.
--> preprocessing 때는 파일 확장자 구분 없이 똑같이 취급
--> Build System은 .c 파일을 compile하므로, 중복 error 발생
Header에 함수를 정의하지 않는 이유
Link 시 중복 함수 정의가 있어서 문제가 됨
헤더 파일에 함수를 정의
+ 여러 소스 파일이 하나의 헤더를 include하는 경우 --> 링크 에러 발생
Sol
- 여러 소스파일들이 include하는 파일들은 반드시 Header와 소스파일로 나누어서 코딩
- 헤더 파일 : 중복 코드가 있어도 상관없는 코드 (함수 선언, 라이브러리 등)
- 소스 파일 : 그 외 코드 삽입
--------
Main Code
- 프로그램 시작 시 동작하는 코드
- 해당 코드는 누군가 호출하지 않음
- Header 파일이 없어도 된다
API Code
- 누군가 호출하는 코드
- 여러 개의 소스파일에서 include 가능하다
- 따라서 , Header 와 Source 파일을 나누어서 구현한다
Static
상황에 따라 역할이 바뀐다.
1) 함수 내 지역변수 앞
// static이 선언된, 이름이 같은 함수 내에서만 변수 재사용 가능
#include <stdio.h>
void abc()
{
static int cnt;
for (int i = 0; i < 5; i++) cnt++;
printf("%d", cnt);
}
void bdf()
{
static int cnt;
cnt += 1;
printf("%d", cnt);
}
int main()
{
abc(); // 5
bdf(); // 1
abc(); // 10
bdf(); // 2
return 0;
}
2) 전역변수 앞 : 다른 소스코드에서 사용 불가
추가로, extern은 여러 번 선언해도 문제 없음
3) 함수 앞 : 다른 소스코드에서 사용 불가 ( 함수는 default가 extern, 우선 순위가 static이 높음)
모든 API가 사용하는 전역변수 생성 방법
1. common.h에 전역변수를 만들고, common.h에 extern을 한다.
2. 전역변수를 사용할 api들은 common.h를 include한다.
Struct
1) 선언은 여러 번 가능, 정의는 1번만 가능
2) 구조체는 타입이므로, extern되지 않는다
3) 변수와 다르게, 헤더 파일에 정의 시 링커가 파일마다 같은 이름의 타입 명을 쓰는 것을 허용
웬만하면 구조체만 모아둔 독립적 헤더파일 사용 권장
Summary
1. 프로젝트
• 소스 파일 코드를 Main Code 와 API Code 로 관리
• API Code 들은 헤더와 소스 파일 세트로 관리
• Main Code 에는 헤더 파일이 없어도 상관 없음
2. 헤더 파일에 넣을 것
• Header Guard [필수]
• 최소한의 #include
• 함수선언 (공개하고 싶은 함수만)
• extern 변수
3. 소스 파일에 넣을 것
• 전역변수
• 나머지 #include
• 나머지 소스코드들