Цифровой сад — общедоступная персональная база знаний (временно садовничаю в другом саду)

Лупа для поиска
  • Свежие правки
  • Карта сайта
  • 8. Разобрать / Информационные технологии / Разработка ПО / Язык программирования C++ /

    Жизненный цикл программы на C++

    Обязательно содержит три этапа:

    1. кодирование программы;
    2. сборка программы;
    3. запуск программы.

    Необязательными этапами могут быть:

    1. Кодирование программы

    Это

    Необязательным результатом кодирования могут является

    Пример исходного кода программы на C++ в файле program.cpp.

    #include <iostream>
    
    int main() {
        std::cout << "Hello, World!" << std::endl;
        return 0;
    }

    2. Сборка программы

    Это

    Состав компилятора g++

    Эти программы можно использовать явно, но рекомендуются вызывать неявно через g++.

    Зарезервированные расширения файлов

    Преобразование исходного кода в машинный

    Зачастую преобразования можно выполнять как конкретными программами (cpp, as, ld) так и при помощи обёртки - g++. Далее, в двух колонках таблицы указываются альтернативные варианты.

    Некоторые полезные параметры g++:

    Обычно, для сборки программы из исходного кода в исполняемый достаточно одной команды.

    g++ -o program program.cpp

    Но мы в учебных целях разберём каждый шаг. Всего их четыре:

    1. Препроцессинг;
    2. Компиляция;
    3. Ассемблирование;
    4. Компоновка.

    2.1 Препроцессинг

    Шаг предварительной обработки заменяет в исходном коде макросы вида #include, #define и прочие #... на конкретные значения. Например, вместо #include <iostream> вставляется исходный код библиотеки для работы с вводом и выводом.

    cpp g++
    $ cpp program.cpp > program.ii $ g++ -E -o program.ii program.cpp

    Где,

    Пример program.ii.

    # 0 "program.cpp"
    # 0 "<built-in>"
    # 0 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    # 0 "<command-line>" 2
    # 1 "program.cpp"
    # 1 "/usr/include/c++/12/iostream" 1 3
    # 36 "/usr/include/c++/12/iostream" 3
    ...
    # 3 "program.cpp"
    int main() {
        std::cout << "Hello, World!" << std::endl;
        return 0;
    }

    2.2 Компиляция

    Шаг компиляции преобразует исходный код на языке C++ в исходный код на языке Ассемблер.

    g++
    $ g++ -S -o program.s program.ii

    Где,

    Пример program.s.

      .file "program.cpp"
      .text
      .local _ZStL8__ioinit
      .comm _ZStL8__ioinit,1,1
      .section .rodata
    .LC0:
      .string "Hello, World!"
      .text
      .globl main
      .type main, @function
    main:
    .LFB1761:
      .cfi_startproc
      pushq %rbp
    ...

    2.3 Ассемблирование

    Шаг ассемблирования преобразует исходный код на языке Ассемблер в машинный код (объектный файл).

    as g++
    $ as -o program.o program.s $ g++ -c -o program.o program.s

    Где,

    Пример машинного кода после дизассемблирования файла program.o можно посмотреть командой objdump --disassemble.

    $ objdump --disassemble program.o
    ...
    Disassembly of section .text:
    
    0000000000000000 <main>:
       0:	55                   	push   %rbp
       1:	48 89 e5             	mov    %rsp,%rbp
       4:	48 8d 05 00 00 00 00 	lea    0x0(%rip),%rax        # b <main+0xb>
       b:	48 89 c6             	mov    %rax,%rsi
       e:	48 8d 05 00 00 00 00 	lea    0x0(%rip),%rax        # 15 <main+0x15>
      15:	48 89 c7             	mov    %rax,%rdi
      18:	e8 00 00 00 00       	call   1d <main+0x1d>
      1d:	48 8b 15 00 00 00 00 	mov    0x0(%rip),%rdx        # 24 <main+0x24>
      24:	48 89 d6             	mov    %rdx,%rsi
      27:	48 89 c7             	mov    %rax,%rdi
      2a:	e8 00 00 00 00       	call   2f <main+0x2f>
      2f:	b8 00 00 00 00       	mov    $0x0,%eax
      34:	5d                   	pop    %rbp
      35:	c3                   	ret
    ...

    Таблицу символов в объектном файле можно посмотреть командой nm.

    $ nm program.o
                     U __cxa_atexit
                     U __dso_handle
                     U _GLOBAL_OFFSET_TABLE_
    0000000000000088 t _GLOBAL__sub_I_main
    0000000000000000 T main
    0000000000000036 t _Z41__static_initialization_and_destruction_0ii
                     U _ZNSolsEPFRSoS_E
                     U _ZNSt8ios_base4InitC1Ev
                     U _ZNSt8ios_base4InitD1Ev
                     U _ZSt4cout
                     U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    0000000000000000 b _ZStL8__ioinit
                     U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc

    Все эти странные символы вокруг имён методов описывают их сигнатуру в специальном формате.

    2.4 Компоновка

    Шаг компоновки связывает несколько файлов машинного кода в единую программу.

    ld g++
    См. ниже $ g++ -o program program.s
    ld \
      --dynamic-linker=/lib64/ld-linux-x86-64.so.2 \
      -o program \
      program.o \
      -L /usr/lib/gcc/x86_64-linux-gnu/12 \
      -l stdc++ -l c \
      /usr/lib/x86_64-linux-gnu/Scrt1.o \
      /usr/lib/x86_64-linux-gnu/crti.o \
      /usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o \
      /usr/lib/gcc/x86_64-linux-gnu/12/crtendS.o \
      /usr/lib/x86_64-linux-gnu/crtn.o

    Обратим внимание, что компоновка при помощи g++ намного лаконичнее, чем ld. Дело в том, что g++ скрывает от пользователя внутреннюю кухню связанную с подключением стандартной библиотеки.

    Как же я узнал, как параметризовать программу ld? Для этого собрал исполняемый файл командой g++ -v -o program program.cpp с параметром -v и там подсмотрел параметры связывания.

    Командой ldd можно посмотреть динамически подключаемые библиотеки, от которых зависит программа.

    $ ldd program
      linux-vdso.so.1 (0x00007ffc33ea7000)
      libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ff1e3800000)
      libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff1e361f000)
      libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff1e353f000)
      /lib64/ld-linux-x86-64.so.2 (0x00007ff1e3a53000)
      libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff1e351f000)

    3. Запуск программы

    Это загрузка скомпонованного машинного кода (исполняемого файла) программы в память компьютера и его запуск.

    Пример запуска программы program.

    $ ./program 
    Hello, World!

    Ресурсы


    1. IDE - интегрированная среда разработки, которая упрощает решение задач разработки↩︎