IT_Study/Operating System

Operating System (10) : C에서의 컴파일 과정, Header & Source 정의, Static & Extern, 구조체 정의

__Vivacé__ 2023. 3. 30. 12:57

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;
}

좌 : 위 코드를 그대로 Compile 했을 경우 / 우 : #define KFC를 Hiding했을 경우

 

조건부 컴파일 지시자 (#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 파일 내 코딩

다음과 같은 경우는 빌드가 가능할까?

좌 : 중복 include 문제 /&nbsp; 우 : 상호 include 사용으로 인한 문제

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이 높음)

abc 사용 불가

 

 

모든 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
    • 나머지 소스코드들