notes

Компиляция

Table of Content

Стадии компиляции

  1. Препроцессор
    • Язык препроцессора - это специальный язык программирования, встроенный с С++ю
    • Препроцессор работает с кодом на С++ как с текстом.
    • Команды языка препроцессор называют директивами, все директивы начинаются со знака #.
    • Директива #include позволяет подключать заголовочные файлы к файлам кода.
      • #include <foo.h> - библиотечный заголовочный файл
      • #include "foo.h" - локальный заголовочный файл
    • Препроцессор заменяет директиву #include "bar.h" на содержимое файла bar.h.
  2. Компиляция
    • На вход компилятору поступает код на С++ после обработки препроцессором.
    • Каждый файл с кодом компилируется отдельно и независимо от других файлов с кодом.
    • Компилируются только файлы с кодом (т.е. *.срр).
    • Заголовочные файлы сами по себе ни во что не компилируются, только в составе файлов с кодом.
    • На выходе компилятора из каждого файла с кодом получается объектный файл - бинарный файл со скомпилированным кодом (с расширением .o или .obj)
  3. Линтовка (компоновка)
    • На этом этапе все объектные файлы объединияются в один исполняемый (или библиотечный) файлю
    • При этом происходит подстановка адресов функций в места их вызова.
        void foo()
        {
            bar(); // call <address of bar>, e.g. call 0x087A
        }
        void bar() { }
      
    • По каждому объектному файлу строится таблица функций, которые в нём определены.
    • На этапе компоновки важно, что каждая функция имеет уникальное имя.
    • В С++ может быть две функции с одним именем, но разными параметрами. Для того чтобы функции имели уникальные имена они искажаются (mangle) таким образом, что в их имени кодируются их параметры. Например, комплятора GCC превратит имя функции foo
        void foo(int, double) {} // _Z3foooid
      
    • Аналогично в линтовке нуждаются глобальные переменные.
    • Точка входа - функция, вызываемая при запуске программы. По умолчанию это функция main.
        int main()
        {
            return 0;
        }
      

      или

        int main(int argc, char ** argv)
        {
            return 0;
        }
      
    • Ошибки линковки:
      • undefined reference - функции имеет объявление, но не имеет тела
      • multiple definition - функция имеет два или более определений (часто возникает в ситуации когда функция определена в заголовочном файле, который включен в несколько .cpp файлов)

g++

  1. g++ - обёртка над набором программ (препроцессор, компилятор и линтовщик). Запускает нужную утилиту или последовательность утилит в зависимости от входных параметров.
  2. Запустить препроцессор на файле:
     g++ -E main.cpp -o main_preprocessed.cpp
    
  3. Запуск компилятора (на выходе получаем объектный файл с расширением .o)
     g++ -с main.cpp
    
  4. Компоновка
     g++ file1.o main.o -o programm
    
  5. Для того чтобы запустить препроцессор, компилятор и компоновщик одной командой
     g++ file1.cpp main.cpp -o programm
    
  6. Чтобы посмотреть содержание объектного файла в виде ассемблера
     g++ -S main.cpp