소스 코드에서 바이너리 파일로: C++ 프로그램 여행

만약 네가 아직 모른다면, 나는 아은보다 더 나아가, 나는 C++를 사랑한다.본고에서 나는 C++가 어떻게 프로그램을 2진 파일로 컴파일하는지, 그리고 왜 내가 C++를 좋아하는지 소개했다.이 부분은 C++가 어떻게 작동하는지 이해하기 위해서입니다.문서는 내가 이해하는 데 도움을 준다.

There are only two kinds of languages: the ones people complain about and the ones nobody uses.
― Bjarne Stroustrup, The C++ Programming Language


나는 호모왕CRUST의 계발을 받아서 C를 위해 자신의 컴파일러를 작성하고 싶다. 나는 러스트를 계속 사용할 것이다.그리고 shoutoutShivyC, 이것이 바로 나의 생각의 근원이다.
파이프를 컴파일하기 시작합시다!

위에서 보신 것은 NerdyElectronics.com로부터의 번역 흐름입니다.
본고에서 우리는 미리 정의된 값을 가진 간단한 덧셈 문제를 사용할 것이다.
//a.cpp program
#include <iostream>
using namespace std;

int main()
{
    int firstNumber = 2, secondNumber =4, sumOfTwoNumbers;

    // sum of two numbers in stored in variable sumOfTwoNumbers
    sumOfTwoNumbers = firstNumber + secondNumber;

    // Prints sum 
    cout << firstNumber << " + " <<  secondNumber << " = " << sumOfTwoNumbers;     

    return 0;
}
만약 네가 도표로 돌아간다면, 너는 우리가 현재 예처리 단계에 있다는 것을 볼 수 있을 것이다.빨리 봅시다Translation Unit.번역 단원은 헤더 파일과 확장 매크로를 포함하면 컴파일러에 입력합니다.
다음 명령을 사용하여 번역 단원을 저장할 수 있습니다
g++ <filename>.cpp -E
쓰레기장이 이렇게 보여요.
# 1 "a.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "a.cpp"
# 1 "c:\\mingw\\lib\\gcc\\mingw32\\8.2.0\\include\\c++\\iostream" 1 3
# 36 "c:\\mingw\\lib\\gcc\\mingw32\\8.2.0\\include\\c++\\iostream" 3

# 37 "c:\\mingw\\lib\\gcc\\mingw32\\8.2.0\\include\\c++\\iostream" 3

# 1 "c:\\mingw\\lib\\gcc\\mingw32\\8.2.0\\include\\c++\\mingw32\\bits\\c++config.h" 1 3
# 236 "c:\\mingw\\lib\\gcc\\mingw32\\8.2.0\\include\\c++\\mingw32\\bits\\c++config.h" 3

# 236 "c:\\mingw\\lib\\gcc\\mingw32\\8.2.0\\include\\c++\\mingw32\\bits\\c++config.h" 3
namespace std
{
  typedef unsigned int size_t;
  typedef int ptrdiff_t;

이것은 너무 길어서 여기에 발표할 수 없습니다. (실제로 stdio 헤더 파일을 추가했기 때문에, 이것은 1k 줄 코드와 같기 때문입니다.) 그러나 궁금하면 시스템에서 실행할 수 있습니다.

조립 번호


좀 돌아봐, 하드웨어에 가까울수록 빨라진다는 소문이 있어.비록 이것은 일리가 있지만, 파이썬과 같은'느린'언어는 통상적으로 속도가 느리다. 왜냐하면 그들은 해석되거나 동적 유형으로 인해 대량의 메모리를 차지하기 때문이다.Python-to-C/C++ 컴파일러가 많고, Python을 더 빨리 작성할 수 있는 항목도 많습니다.하느님을 봐서 어떤 언어로 어떤 것을 개발하지 마라. 왜냐하면 그것은 하드웨어에 더 가깝기 때문이다.
어쨌든, 지금 우리 a.cpp 파일에서 이걸 실행하고 있습니다.
gpp a.cpp -S
지금 너는 이런 게 있을 거야.
    .file   "a.cpp"
    .text
    .section .rdata,"dr"
__ZStL19piecewise_construct:
    .space 1
.lcomm __ZStL8__ioinit,1,1
    .def    ___main;    .scl    2;  .type   32; .endef
LC0:
    .ascii " + \0"
LC1:
    .ascii " = \0"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB1502:
    .cfi_startproc
    leal    4(%esp), %ecx
    .cfi_def_cfa 1, 0
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    .cfi_escape 0x10,0x5,0x2,0x75,0
    movl    %esp, %ebp
    pushl   %ecx
    .cfi_escape 0xf,0x3,0x75,0x7c,0x6
    subl    $36, %esp
    call    ___main
    movl    $2, -12(%ebp)
    movl    $4, -16(%ebp)
    movl    -12(%ebp), %edx
    movl    -16(%ebp), %eax
    addl    %edx, %eax
    movl    %eax, -20(%ebp)
    movl    -12(%ebp), %eax
    movl    %eax, (%esp)
    movl    $__ZSt4cout, %ecx
    call    __ZNSolsEi
    subl    $4, %esp
    movl    $LC0, 4(%esp)
    movl    %eax, (%esp)
    call    __ZStlsISt11char_
다시 한 번 말하지만, 시간이 너무 길어, 너 자신의 시스템에서 해 봐!이것은 당신의 목표 구조를 위해 구축될 것입니다.연습으로 당신의 시스템 구조가 무엇인지 알아보세요!이제 각 아키텍처마다 다른 명령 집합이 있습니다. 프로세서는 이 명령 집합을 이해할 수 있습니다. 컴파일러는 이를 여러 프로세스로 분해합니다.
  • 생성Abstract Syntax Tree
  • 아키텍처와 관련된 명령 생성
  • 이것이 무엇을 의미하는지 봅시다.

    추상 문법 트리


    추상적 문법 트리는 매우 좋으며 목표 구조에서 추상적으로 나온 것이다.그러나 이것은 이 용어의'추상'부분의 출처가 아니다.Wikipedia에 따르면 요약은 이러한 사실을 가리킨다. "그것은 실제 문법에 나타난 모든 세부 사항을 가리키는 것이 아니라 구조나 내용과 관련된 세부 사항을 가리킨다."AST는 구문 분석 후에 생성됩니다.모든 프로그램에서 AST를 생성할 수 있습니다.우리의 코드에 대해 이것이 바로 AST의 모습이다

    다음은 너 혼자 어떻게 하는 거야?
    g++ -fdump-tree-all-graph a.cpp -o a
    dot -Tpng a.cpp.013t.cfg.dot -o a.png
    
    이것은 GraphViz를 사용하여 구축된 것이므로 명령줄에 설치하십시오.또한 모든 온라인 GraphViz 시각화 도구에서 붙여넣기a.cpp.013t.cfg.dot의 내용을 복사할 수 있습니다.

    객체 파일 및 링크


    목표 파일에는 목표 코드가 있는데 본질적으로 기계 코드(또는 일부 중간 코드)이다.이것은 당신이 본 고전 저작 article 에서 보듯이 번역 과정의 대상입니다.내가 그 기이한'번역 과정 단계'도표를 사용하지 않은 것은 번역의 실제 과정을 약간 추상했기 때문이다.적당한 때에 우리도 이 문제를 토론할 것이다.계속하기 전에 를 사용하여 객체(.o) 파일을 작성합니다.
    g++ a.cpp -c
    
    이제 링크를 살펴보겠습니다. 대상 파일을 만든 후에 링크를 진행합니다.다른 실행 가능한 객체 파일을 만들기 위해 객체 파일이 함께 링크됩니다.이를 위해 프로그램을 헤더 파일과 주 CPP 파일로 나눕니다.
    //a.h
    #include <stdio.h>
    void printLinker()
    {
        printf("Hello World");
    }
    
    이제 다른 파일에서 호출합시다
    #include "a.h"
    int main()
    {
        printLinker();
        return 0;
    }
    
    마지막으로 링크를 표시하기 위해 다른 원본 파일을 만듭니다.
    //We will name this a2.cpp
    void printLinker();
    
    a.cpp를 컴파일하면 예상대로 하나 Hello World 를 줄 것입니다.그런데 링크를 봐야 돼요. 그렇죠?
    g++ a.h -c
    g++ a.cpp -c
    g++ a2.cpp -c
    
    이제 우리는 하나가 있다.obj와 a.gch (예정된 헤더, 만약 찾을 수 없다면, 컴파일러는 헤더를 찾을 것입니다.링크하라고!

    gcc a.o a2.o -o a2.exe
    
    좋아, 우리가 이 두 대상 파일을 어떻게 호출하고 컴파일하는지 봤어?지금 우리는 a2를 실행하기만 하면 된다.exe, 인쇄됩니다 Hello World.
    ./a2.exe
    Hello World
    
    완벽했어이 파일의 내용을 보려면 nm 도구를 사용할 수 있습니다.
    nm a.o
    
    당신은 다음과 같은 정보를 얻을 수 있습니다
    00000000 b .bss
    00000000 d .data
    00000000 r .eh_frame
             U ___main
    00000015 T _main
             U _printf
    00000000 r .rdata
    00000000 r .rdata$zzz
    00000000 t .text
    00000000 T __Z7print_av
    
    다른 대상 파일에 대해 같은 작업을 수행할 수 있습니다!파일에 포함된 내용은 매우 명확해서 잘 분할되었다.
    아이고, 너무 많아. 컴파일러가 이렇게 컴파일했어.나로 하여금 빨리 결론을 얻게 하다.
  • 사전 처리
  • 어셈블리
  • 구성 요소
  • 링크
  • 이렇게, 동료들아!두 번째 부분에서 뵙겠습니다. 여기서 업데이트하겠습니다.

    좋은 웹페이지 즐겨찾기