C 언어는 ".OBJ" 바이너리 파일을 생성하기 위해 컴파일됩니다. C 언어 소스 프로그램이 C 언어 컴파일러에 의해 컴파일되면 ".OBJ"라는 접미사가 붙은 바이너리 파일이 생성됩니다. 마지막으로 "링커"라는 소프트웨어는 이 ".OBJ" 파일을 C 언어 컴파일러에서 제공하는 다양한 프로그램과 결합합니다. C 언어. 라이브러리 함수가 서로 연결되어 ".EXE"라는 접미사가 붙은 파일을 생성합니다.
C언어 소스 프로그램이 C언어 컴파일러에 의해 컴파일되면, 접미사 ".OBJ"를 붙인 바이너리 파일(오브젝트 파일이라고 함)이 생성되고, 최종적으로 ".OBJ"라는 프로그램에 의해 처리됩니다. Link" 소프트웨어는 이 ".OBJ" 파일을 C 언어에서 제공하는 다양한 라이브러리 기능과 연결하여 접미사가 ".EXE"인 실행 파일을 생성합니다. 분명히 C 언어는 즉시 실행될 수 없습니다.
추천 튜토리얼: "c 언어 튜토리얼 비디오"
C언어 파일의 컴파일 및 실행의 4단계가 별도로 설명됩니다.
C언어의 컴파일 및 링크 프로세스 작성된 c 프로그램(소스 코드)을 하드웨어에서 실행할 수 있는 프로그램(실행 코드)으로 변환하려면 컴파일하고 링크해야 합니다. 컴파일은 텍스트 형식의 소스 코드를 기계어 형식의 개체 파일로 번역하는 프로세스입니다. 링크는 대상 파일, 운영 체제 시작 코드 및 사용된 라이브러리 파일을 구성하여 최종적으로 실행 가능한 코드를 생성하는 프로세스입니다. 프로세스 다이어그램은 다음과 같습니다.
그림에서 볼 수 있듯이 전체 코드의 컴파일 프로세스는 두 가지 프로세스로 나누어집니다. 컴파일은 중괄호로 묶인 부분에 해당합니다. 그림을 그리고 나머지는 연결하는 과정입니다.
컴파일 프로세스
컴파일 프로세스는 컴파일과 어셈블리의 두 단계로 나눌 수 있습니다.
컴파일
컴파일은 소스 프로그램(문자 스트림)을 읽고, 이에 대한 어휘 및 구문 분석을 수행하고, 고급 언어 명령을 기능적으로 동등한 어셈블리 코드로 변환하는 것입니다. 소스 파일의 컴파일 프로세스에는 두 가지 주요 단계가 포함됩니다.
첫 번째 단계는 정식 편집 단계 이전에 수행되는 전처리 단계입니다. 전처리 단계에서는 파일에 있는 전처리 지시문에 따라 소스 파일의 내용을 수정합니다. 예를 들어#include 명령어는 헤더 파일의 내용을 .cpp 파일에 추가하는 전처리 명령어입니다. 컴파일 전에 소스 파일을 수정하는 이 방법은 다양한 컴퓨터 및 운영 체제 환경의 제약 조건에 적응할 수 있는 뛰어난 유연성을 제공합니다. 사용 가능한 하드웨어나 운영 체제가 다르기 때문에 한 환경에 필요한 코드는 다른 환경에 필요한 코드와 다를 수 있습니다. 대부분의 경우 다양한 환경에 대한 코드를 동일한 파일에 넣은 다음 전처리 단계에서 코드를 수정하여 현재 환경에 맞게 조정할 수 있습니다. 주로 다음 측면을 다룹니다.
(
1) #define a b와 같은 매크로 정의 지침
이런 종류의 의사 명령어의 경우 사전 컴파일에서 해야 할 일은 프로그램의 모든 a를 b로 바꾸는 것뿐입니다. 그러나 문자열 상수인 a는 대체되지 않습니다. 또한 #undef도 있는데, 이는 특정 매크로의 정의를 취소하여 앞으로 해당 문자열이 더 이상 대체되지 않도록 합니다.
(2) 조건부 컴파일 지침(예: #ifdef, #ifndef, #else, #elif, #endif 등)
이러한 의사 명령어의 도입으로 프로그래머는 다양한 매크로를 정의하여 컴파일러에서 처리할 코드를 결정할 수 있습니다. 프리컴파일러는 관련 파일을 기반으로 불필요한 코드를 필터링합니다.
(3) 헤더 파일에는 #include 'FileName' 또는 #include etc와 같은 지침이 포함되어 있습니다.
헤더 파일에서 의사 명령어 #define는 일반적으로 많은 수의 매크로(가장 일반적인 매크로는 문자 상수)를 정의하는 데 사용되며 다양한 외부 기호 선언도 포함합니다. 헤더 파일을 사용하는 목적은 주로 여러 다른 C 소스 프로그램에서 특정 정의를 사용할 수 있도록 하는 것입니다. 이러한 정의를 사용해야 하는 C 소스 프로그램에서는 이 파일에서 이러한 정의를 반복할 필요 없이 #include 문만 추가하면 됩니다. 사전 컴파일러는 헤더 파일의 모든 정의를 컴파일러에서 처리하기 위해 생성하는 출력 파일에 추가합니다. c 소스 프로그램에 포함된 헤더 파일은 시스템에서 제공할 수 있습니다. 이러한 헤더 파일은 일반적으로 /usr/include 디렉터리에 있습니다. #include 프로그램에서는 꺾쇠 괄호(< >)를 사용합니다. 또한 개발자는 자신만의 헤더 파일을 정의할 수도 있습니다. 이 파일은 일반적으로 c 소스 프로그램과 동일한 디렉터리에 배치됩니다. 이 경우 #include에 큰따옴표('')를 사용해야 합니다.
(4) 특수 기호, 프리컴파일러는 일부 특수 기호를 인식할 수 있습니다.
예를 들어 소스 프로그램에 나타나는 LINE 로고는 현재 줄 번호(십진수)로 해석되고, FILE은 현재 컴파일된 C 소스 프로그램의 이름으로 해석됩니다. 프리컴파일러는 소스 프로그램에서 이러한 문자열의 발생을 적절한 값으로 바꿉니다.
프리컴파일러가 하는 일은 기본적으로 소스 프로그램을 "교체"하는 것입니다. 이 대체 후에는 매크로 정의, 조건부 컴파일 지침 및 특수 기호가 없는 출력 파일이 생성됩니다. 이 파일의 의미는 전처리되지 않은 소스 파일과 동일하지만 내용이 다릅니다. 다음으로, 이 출력 파일은 컴파일러의 출력으로서 기계 명령어로 변환됩니다.
컴파일 및 최적화의 두 번째 단계에서 미리 컴파일된 출력 파일에는 숫자, 문자열, 변수 정의 및 C 언어 키워드(예: main, if, else)와 같은 상수만 포함됩니다. , for, while, {,}, +, -, *, 등등.
컴파일러의 작업은 어휘 분석과 구문 분석을 사용하여 모든 명령어가 문법 규칙을 준수하는지 확인한 다음 이를 동등한 중간 코드 표현이나 어셈블리 코드로 변환하는 것입니다.
최적화 처리는 컴파일 시스템에서 상대적으로 어려운 기술입니다. 이에 관련된 문제는 컴파일 기술 자체뿐만 아니라 기계의 하드웨어 환경과도 많은 관련이 있습니다. 최적화의 일부는 중간 코드의 최적화입니다. 이 최적화는 특정 컴퓨터와 독립적입니다. 또 다른 유형의 최적화는 주로 대상 코드 생성을 목표로 합니다.
전자 최적화의 경우 주요 작업은 공개 표현식 삭제, 루프 최적화(코드 추출, 강도 약화, 루프 제어 조건 변경, 알려진 수량 병합 등), 복사 전파, 쓸모 없는 할당 삭제 등입니다. 기다리다.
후자의 유형의 최적화는 기계의 하드웨어 구조와 밀접한 관련이 있으며, 가장 중요한 고려 사항은 기계의 각 하드웨어 레지스터에 저장된 관련 변수의 값을 어떻게 최대한 활용하여 메모리 수를 줄일 것인지입니다. 액세스합니다. 또한 기계 하드웨어 실행 명령(예: 파이프라인, RISC, CISC, VLIW 등)의 특성에 따라 명령을 일부 조정하여 대상 코드를 더 짧게 만들고 실행을 수행하는 방법을 설명합니다. 효율성을 높이는 것도 중요한 연구 주제이다.
Assembly
어셈블리는 실제로 어셈블리 언어 코드를 대상 기계 명령어로 변환하는 프로세스를 말합니다. 번역 시스템에서 처리되는 모든 C 언어 소스 프로그램에 대해 해당 대상 파일은 결국 이 처리를 통해 획득됩니다. 타겟 파일에 저장되는 것은 소스 프로그램과 동등한 타겟의 기계어 코드이다. 개체 파일은 세그먼트로 구성됩니다. 일반적으로 개체 파일에는 두 개 이상의 섹션이 있습니다.
코드 섹션: 이 섹션에는 주로 프로그램 지침이 포함되어 있습니다. 이 세그먼트는 일반적으로 읽고 실행할 수 있지만 일반적으로 쓸 수는 없습니다.
데이터 세그먼트: 주로 프로그램에서 사용되는 다양한 전역 변수 또는 정적 데이터를 저장합니다. 일반적으로 데이터 세그먼트는 읽기, 쓰기 및 실행이 가능합니다.
UNIX객체 파일에는 세 가지 주요 유형이 있습니다.
(1) 재배치 가능한 파일
실행 파일 또는 공유 객체 파일 및 데이터를 생성하기 위해 다른 객체 파일에 연결하는 데 적합한 코드가 포함되어 있습니다.
(2)공유 객체 파일
이 파일은 두 가지 컨텍스트에서 연결하기에 적합한 코드와 데이터를 저장합니다. 첫 번째는 링커가 다른 재배치 가능한 파일 및 공유 개체 파일과 함께 처리하여 다른 개체 파일을 생성할 수 있다는 것이고, 두 번째는 동적 링커가 이를 다른 실행 파일 및 다른 공유 개체 파일과 결합하여 프로세스를 생성할 수 있다는 것입니다. 영상.
(3) 실행 파일
운영체제에서 생성한 프로세스에서 실행할 수 있는 파일이 들어있습니다. 어셈블러가 생성하는 것은 실제로 첫 번째 유형의 개체 파일입니다. 후자의 두 가지 경우에는 이를 얻기 위해 몇 가지 다른 처리가 필요합니다. 이것이 링커의 작업입니다.
링크 과정
어셈블러에서 생성된 객체 파일은 바로 실행되지 않으며, 해결되지 않은 문제가 많을 수 있습니다.
예를 들어, 소스 파일의 함수는 다른 소스 파일에 정의된 기호(예: 변수 또는 함수 호출 등)를 참조할 수 있습니다. 라이브러리 파일의 함수는 프로그램 등에서 호출될 수 있습니다. 이 모든 문제는 링커를 통해 해결되어야 합니다.
링커의 주된 역할은 관련된 타겟 파일을 서로 연결하는 것, 즉 한 파일에서 참조하는 기호를 다른 파일의 기호 정의와 연결하여 이러한 모든 타겟 파일이 작동 가능한 통합된 파일이 되도록 하는 것입니다. 시스템이 로드하고 실행하는 전체 내용입니다.
개발자가 지정한 동일한 라이브러리 함수의 서로 다른 연결 방법에 따라 링크 처리는 두 가지 유형으로 나눌 수 있습니다.
(1) 정적 링크
이 연결 방법에서 함수의 코드는 해당 위치에서 정적이어야 합니다. 연결된 라이브러리가 최종 실행 프로그램에 복사됩니다. 이러한 방식으로 이러한 코드는 프로그램이 실행될 때 프로세스의 가상 주소 공간에 로드됩니다. 정적 링크 라이브러리는 실제로 개체 파일의 모음이며, 각 개체 파일에는 라이브러리의 관련 기능 하나 또는 그룹에 대한 코드가 포함되어 있습니다.
(2) 동적 링크
이 방법에서는 함수의 코드가 동적 링크 라이브러리 또는 공유 객체라는 객체 파일에 배치됩니다. 이때 링커가 하는 일은 공유 객체의 이름과 기타 소량의 등록 정보를 최종 실행 프로그램에 기록하는 것이다. 이 실행 파일이 실행되면 동적 링크 라이브러리의 전체 내용이 런타임 시 해당 프로세스의 가상 주소 공간에 매핑됩니다. 동적 링커는 실행 프로그램에 기록된 정보를 기반으로 해당 기능 코드를 찾습니다.
실행 파일의 함수 호출에는 동적 링크 또는 정적 링크를 각각 사용할 수 있습니다. 동적 연결을 사용하면 최종 실행 파일을 더 짧게 만들고 여러 프로세스에서 공유 객체를 사용할 때 일부 메모리를 절약할 수 있습니다. 왜냐하면 이 공유 객체에 대한 코드 복사본 하나만 메모리에 저장하면 되기 때문입니다. 그러나 이것이 반드시 동적 링크를 사용하는 것이 정적 링크를 사용하는 것보다 우수하다는 것을 의미하지는 않습니다. 어떤 경우에는 동적 연결로 인해 성능이 저하될 수 있습니다.
우리가 linux에서 사용하는 gcc 컴파일러는 사용자가 단 하나의 명령으로 컴파일 작업을 완료할 수 있도록 번들로 제공합니다. 이는 실제로 컴파일 작업을 용이하게 하지만 초보자가 컴파일을 이해하기에는 매우 불리한 과정입니다. 아래 그림은 gccagent의 컴파일 과정입니다:
위 그림에서 볼 수 있듯이:
Precompile
.c 파일을 .i 파일
으로 변환합니다. 사용된 gcc 명령은 다음과 같습니다: gcc –E
전처리 명령에 해당 cpp
컴파일
.c/.h 파일을 .s 파일로 변환
사용된 gcc 명령은 다음과 같습니다. gcc –S
컴파일 명령 cc –S
에 해당어셈블리
.s 파일을 .o 파일로 변환
사용된 gcc 명령은 다음과 같습니다. gcc –c
해당 어셈블리 명령은 as
입니다. 링크
. 파일은 실행 가능한 프로그램으로 변환됩니다
사용된 gcc 명령은 gcc
해당 링크 명령은 ld
정리하면 컴파일 과정은 위의 4가지 프로세스입니다: 사전 컴파일, 컴파일 , 조립, 링크. Lia이 네 가지 프로세스에서 수행되는 작업을 이해하는 것은 헤더 파일, 라이브러리 등의 작업 프로세스를 이해하는 데 도움이 되며, 컴파일 및 링크 프로세스에 대한 명확한 이해는 프로그래밍할 때 오류를 찾고, 시도하는 데에도 도움이 됩니다. 프로그래밍할 때 최선을 다하면 컴파일러의 오류 감지가 큰 도움이 될 수 있습니다.
위 내용은 C 언어로 컴파일하면 어떤 파일이 생성되나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!