분할 컴파일시 유의사항.
사전 지식
compile의 전체 과정
- 전처리(Preprocessing): 이 단계에서는 #include, #define, #if 등과 같은 전처리 지시어가 처리됩니다. #include 지시어를 통해 헤더 파일이 소스 코드 파일에 포함되며, #define 지시어는 매크로를 정의하고, #if 지시어는 조건 컴파일을 수행합니다.
- 컴파일(Compilation): 이 단계에서는 전처리된 소스 코드가 컴파일러에 의해 기계 독립적인 중간 코드(어셈블리 코드)로 변환됩니다. 컴파일러는 이 단계에서 소스 코드의 문법적 오류를 검사하며, 타입 체크와 최적화를 수행합니다.
- 어셈블리(Assembly): 이 단계에서는 컴파일러가 생성한 어셈블리 코드가 기계어로 변환됩니다. 이 과정에서 생성되는 기계어 코드는 플랫폼과 시스템 아키텍처에 종속적입니다.
- 링킹(Linking): 이 단계에서는 여러 개의 오브젝트 파일(.o 또는 .obj 파일)과 라이브러리(.lib, .dll, .so 등)가 모여 하나의 실행 파일을 생성합니다. 링커는 모든 함수 호출이 해당 함수의 정의와 매치되는지 확인하며, 이 단계에서 정의되지 않은 함수나 변수를 참조하는 경우 오류가 발생합니다.
위의 모든 단계를 거쳐 실행 가능한 바이너리 파일이 생성되는 것이 전체 컴파일 과정입니다.
Header 와 source 파일간의 의존성 관계
- 나는 프로젝트 관리를 위하여, 구조체, 함수선언, 함수 정의를 모두 다른 파일에 만들어 놓는다. 이유는 그래야 관리가 편하기 때문이다.
- 프로그래밍을 하다보면 많은 구조체를 정의했는데, 함수 선언부에서 해당 구조체를 참조하지 못하여 에러가 나는 경우가 종종 발생한다.
우선 의존성의 관계가 완벽한 소스코드를 보면 다은과 같다
- 파일의 구조
- 동일한 디렉토리에 좌측 화면부터 보면 main.c function.c b.h a.h 가 존재한다.
- main.c 메인함수
- function.c 사용자 함수 정의
- b.h 상용자 함수 선언
- a.h 구조체 선언
위의 파일들은 gcc -o test main.c function.c 로 컴파일하면 정상 컴파일이 수행된다.
c파일에서 header파일이 없을 때 발생하는 에러
만약 function.c 에서 "./a.h" 헤더가 빠지게 되면 다음과 같은 에러가 발생한다.
function.c:3:20: error: expected ')' before '*' token
헤더 파일간의 의존선 관계
헤더 파일의 순서가 잘못 되었을 경우 발생하는 에러
./b.h:4:20: error: expected ')' before '*' token
이와 같은 에러가 발생하는 이유는 구조체를 사용하는 함수 선언부가 구조체 선어보다 먼저 사용되었기 때문이다.
그러므로 헤더순서도 분할 컴파일에서는 주의해야한다.
위와 같은 현상이 이러나는 이유
컴파일 과정을 이해하지 못하고 있기 때문이다
컴파일 과정 설명
GCC에서 소스 프로그램을 컴파일할때 가장먼저 전처리 프로세서 동작한다. 전처리 단계에서는 필요한 헤더파일을 삽입하고, 메크로 상수들을 변환한다. 전처리 과정이 끝나면 컴파일단계1을 거쳐 .s라는 어셈블리 파일을 만든다. 컴파일된 어셈블러 파일을 다시 어셈블러를 거쳐 기계가이해 할수 있는 오브젝트 파일을 만든다. 이렇게 만들어진 오브젝트 파일들은 라이브러리 파일과 함께 링킹(linking) 과정(ld)를 통해 결합 되어 하나의 실행 가능한 실행 파일로 만들어진게 된다.
- 상기 예제에서 발생한 에러들은 헤더 파일을 빠뜨리거나, 헤더파일의 순서가 잘된 경우이다.
오브젝트 링킹에 따른 에러 발생
undefined reference to
VS로만 코딩해서 몰랐는데 리눅스는 makefile에서
디펜던시를 고려하여 라이브러리 순서를 잘 써줘야 한다. 가장 의존성이 큰 것을 맨 왼쪽에,
가장 의존성이 낮은 것을 오른 쪽에 써야 한다.
가령 libchild.lib가 libmother.lib를 사용한다면,
-lchild -lmother 이런 순서로 하지 않으면 필요한 함수를 못 찾는다.
- 오브젝트 링킹이 잘못 되어 발생하는 에러는 아래와 같이 undefined reference라고 뜨며 이는 call된 함수를 정의한 파일이 컴파일 되지 않았음을 의미한다.
/tmp/ccPWDFU3.o: In function `main':
main.c:(.text+0x17): undefined reference to `call_info'
collect2: ld returned 1 exit status
정리
개별 object 컴파일의 핵심은 function header가 꼭 필요한 다는 것이고
link 과정의 핵심은 function define이 꼭 필요하다는 것이다.
'프로그래밍 > 리눅스 프로그래밍' 카테고리의 다른 글
[bash] 가장 혼란 스러운 문법정리 (0) | 2018.03.08 |
---|---|
메모리 맵 관련 함수 (0) | 2017.05.04 |
리눅스 프로세스별 메모리 사용량 확인법 (0) | 2017.01.24 |
Bash Script 유용한 Tip (0) | 2016.12.27 |
ssh 원격 명령 실행 (0) | 2016.12.27 |