C 프로그램의 컴파일 프로세스는 여러 단계로 구성됩니다: 전처리(preprocessing), 컴파일(compilation), 어셈블리(assembling), 링킹(linking). 역사적으로 이러한 구조는 도구 간의 책임을 분리하고 빌드 프로세스의 유지 관리 및 조정을 용이하게 했습니다.
문제: 각 단계가 어떻게 작동하는지 이해하지 못하면 "정의되지 않은 참조(undefined reference)"와 같은 오류, 코드 중복, 매크로의 잘못된 사용으로 인한 비논리적인 버그 및 여러 파일로의 코드 확장 시 문제가 발생할 수 있습니다.
해결책: 각 단계가 특정 기능을 수행한다는 것을 이해해야 합니다: 전처리기는 #define, #include 및 기타 지시문을 처리하고, 컴파일러는 C 소스를 어셈블리 코드로 변환하며, 어셈블러는 이를 기계 코드로 변환하고, 링커는 모든 오브젝트 파일과 라이브러리를 최종 실행 파일로 결합합니다.
코드 예시:
소스 코드 조각:
#include <stdio.h> #define PI 3.14 int main() { printf("%f ", PI); return 0; }
단계별로 명시적으로 gcc 호출 예시:
gcc -E program.c # 전처리 gcc -S program.c # 컴파일(어셈블리 이전) gcc -c program.c # 어셈블리(오브젝트 파일 생성) gcc program.o -o prog # 링킹
주요 특징:
전처리 단계에서 #include "file.h" 지시문이 하는 일은 무엇인가요?
#Include는 전처리 단계에서 호출 위치에 파일 내용을 직접 삽입합니다. 동일한 파일을 두 번 포함하면 객체의 중복 정의 또는 링킹 오류가 발생할 수 있습니다.
하나의 파일에서 함수 정의만으로 다른 파일에서 사용할 수 있나요?
아니요, 헤더 파일에 선언(프로토타입)이 필요하거나 적어도 extern 선언이 필요합니다. 그렇지 않으면 링킹 시 "암시적 선언(implicit declaration of function)" 또는 "정의되지 않은 참조(undefined reference)" 오류가 발생할 수 있습니다.
static으로 선언된 변수를 다른 파일에서 사용할 수 있나요?
아니요. static은 변수를 현재 파일로 제한합니다. 이는 이러한 심볼이 다른 오브젝트 파일에서 링커에 의해 보이지 않음을 의미합니다.
초보자가 두 개의 소스 파일에서 동일한 이름의 함수를 구현합니다. 링킹 단계에서 "multiple definition of function"이라는 이상한 오류가 발생합니다.
장점:
단점:
선언을 위한 .h 파일 및 정의를 위한 .c 파일 생성. 헤더 파일 보호를 위해 #ifdef 사용. 모든 파일은 링커를 통해 최종 프로그램으로 연결됩니다.
장점:
단점: