- Процесс компиляции программ на C++
- Состав компилятора g++
- Зачем нужно компилировать исходные файлы?
- Этапы компиляции:
- 1) Препроцессинг
- 2) Компиляция
- 3) Ассемблирование
- 4) Компоновка
- 5) Загрузка
- Заключение
- Пошаговое руководство: Создание стандартной программы C++ (C++)
- Предварительные требования
- Создание проекта и добавление исходного файла
- Создание проекта C++ в Visual Studio
- Создание проекта C++ в Visual Studio 2017
- Создание проекта C++ в Visual Studio 2015
- Добавление нового исходного файла
- Как скомпилировать CPP в EXE
Процесс компиляции программ на C++
В данной статье я хочу рассказать о том, как происходит компиляция программ, написанных на языке C++, и описать каждый этап компиляции. Я не преследую цель рассказать обо всем подробно в деталях, а только дать общее видение. Также данная статья — это необходимое введение перед следующей статьей про статические и динамические библиотеки, так как процесс компиляции крайне важен для понимания перед дальнейшим повествованием о библиотеках.
Все действия будут производиться на Ubuntu версии 16.04.
Используя компилятор g++ версии:
$ g++ --version g++ (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609
Состав компилятора g++
Мы не будем вызывать данные компоненты напрямую, так как для того, чтобы работать с C++ кодом, требуются дополнительные библиотеки, позволив все необходимые подгрузки делать основному компоненту компилятора — g++.
Зачем нужно компилировать исходные файлы?
Исходный C++ файл — это всего лишь код, но его невозможно запустить как программу или использовать как библиотеку. Поэтому каждый исходный файл требуется скомпилировать в исполняемый файл, динамическую или статическую библиотеки (данные библиотеки будут рассмотрены в следующей статье).
Этапы компиляции:
Перед тем, как приступать, давайте создадим исходный .cpp файл, с которым и будем работать в дальнейшем.
#include using namespace std; #define RETURN return 0 int main()
1) Препроцессинг
Самая первая стадия компиляции программы.
Препроцессор — это макро процессор, который преобразовывает вашу программу для дальнейшего компилирования. На данной стадии происходит происходит работа с препроцессорными директивами. Например, препроцессор добавляет хэдеры в код (#include), убирает комментирования, заменяет макросы (#define) их значениями, выбирает нужные куски кода в соответствии с условиями #if, #ifdef и #ifndef.
Хэдеры, включенные в программу с помощью директивы #include, рекурсивно проходят стадию препроцессинга и включаются в выпускаемый файл. Однако, каждый хэдер может быть открыт во время препроцессинга несколько раз, поэтому, обычно, используются специальные препроцессорные директивы, предохраняющие от циклической зависимости.
Получим препроцессированный код в выходной файл driver.ii (прошедшие через стадию препроцессинга C++ файлы имеют расширение .ii), используя флаг -E, который сообщает компилятору, что компилировать (об этом далее) файл не нужно, а только провести его препроцессинг:
Взглянув на тело функции main в новом сгенерированном файле, можно заметить, что макрос RETURN был заменен:
В новом сгенерированном файле также можно увидеть огромное количество новых строк, это различные библиотеки и хэдер iostream.
2) Компиляция
На данном шаге g++ выполняет свою главную задачу — компилирует, то есть преобразует полученный на прошлом шаге код без директив в ассемблерный код. Это промежуточный шаг между высокоуровневым языком и машинным (бинарным) кодом.
Ассемблерный код — это доступное для понимания человеком представление машинного кода.
Используя флаг -S, который сообщает компилятору остановиться после стадии компиляции, получим ассемблерный код в выходном файле driver.s:
.file "driver.cpp" .local _ZStL8__ioinit .comm _ZStL8__ioinit,1,1 .section .rodata .LC0: .string "Hello, world!" .text .globl main .type main, @function main: .LFB1021: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $.LC0, %esi movl $_ZSt4cout, %edi call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi movq %rax, %rdi call _ZNSolsEPFRSoS_E movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1021: .size main, .-main .type _Z41__static_initialization_and_destruction_0ii, @function _Z41__static_initialization_and_destruction_0ii: .LFB1030: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl %edi, -4(%rbp) movl %esi, -8(%rbp) cmpl $1, -4(%rbp) jne .L5 cmpl $65535, -8(%rbp) jne .L5 movl $_ZStL8__ioinit, %edi call _ZNSt8ios_base4InitC1Ev movl $__dso_handle, %edx movl $_ZStL8__ioinit, %esi movl $_ZNSt8ios_base4InitD1Ev, %edi call __cxa_atexit .L5: nop leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1030: .size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii .type _GLOBAL__sub_I_main, @function _GLOBAL__sub_I_main: .LFB1031: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $65535, %esi movl $1, %edi call _Z41__static_initialization_and_destruction_0ii popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1031: .size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main .section .init_array,"aw" .align 8 .quad _GLOBAL__sub_I_main .hidden __dso_handle .ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609" .section .note.GNU-stack,"",@progbits
Мы можем все также посмотреть и прочесть полученный результат. Но для того, чтобы машина поняла наш код, требуется преобразовать его в машинный код, который мы и получим на следующем шаге.
3) Ассемблирование
Так как x86 процессоры исполняют команды на бинарном коде, необходимо перевести ассемблерный код в машинный с помощью ассемблера.
Ассемблер преобразовывает ассемблерный код в машинный код, сохраняя его в объектном файле.
Объектный файл — это созданный ассемблером промежуточный файл, хранящий кусок машинного кода. Этот кусок машинного кода, который еще не был связан вместе с другими кусками машинного кода в конечную выполняемую программу, называется объектным кодом.
Далее возможно сохранение данного объектного кода в статические библиотеки для того, чтобы не компилировать данный код снова.
Получим машинный код с помощью ассемблера (as) в выходной объектный файл driver.o:
Но на данном шаге еще ничего не закончено, ведь объектных файлов может быть много и нужно их всех соединить в единый исполняемый файл с помощью компоновщика (линкера). Поэтому мы переходим к следующей стадии.
4) Компоновка
Компоновщик (линкер) связывает все объектные файлы и статические библиотеки в единый исполняемый файл, который мы и сможем запустить в дальнейшем. Для того, чтобы понять как происходит связка, следует рассказать о таблице символов.
Таблица символов — это структура данных, создаваемая самим компилятором и хранящаяся в самих объектных файлах. Таблица символов хранит имена переменных, функций, классов, объектов и т.д., где каждому идентификатору (символу) соотносится его тип, область видимости. Также таблица символов хранит адреса ссылок на данные и процедуры в других объектных файлах.
Именно с помощью таблицы символов и хранящихся в них ссылок линкер будет способен в дальнейшем построить связи между данными среди множества других объектных файлов и создать единый исполняемый файл из них.
Получим исполняемый файл driver:
$ g++ driver.o -o driver // также тут можно добавить и другие объектные файлы и библиотеки
5) Загрузка
Последний этап, который предстоит пройти нашей программе — вызвать загрузчик для загрузки нашей программы в память. На данной стадии также возможна подгрузка динамических библиотек.
Заключение
В данной статье были рассмотрены основы процесса компиляции, понимание которых будет довольно полезно каждому начинающему программисту. В скором времени будет опубликована вторая статья про статические и динамические библиотеки.
Пошаговое руководство: Создание стандартной программы C++ (C++)
Вы можете использовать Visual Studio для создания программ C++ уровня «Стандартный». Выполнив действия, описанные в этом пошаговом руководстве, вы можете создать проект, добавить новый файл в проект, изменить файл, чтобы добавить код C++, а затем скомпилировать и запустить программу с помощью Visual Studio.
Вы можете ввести собственную программу C++ или использовать один из примеров программ. Пример программы в этом пошаговом руководстве является консольным приложением. Это приложение использует set контейнер в стандартной библиотеке C++.
Если требуется соответствие определенной версии стандарта языка C++ (например, C++14, C++17 или C++20), используйте параметр компилятора /std:c++14 , /std:c++17 или /std:c++20 . (Visual Studio 2017 и более поздние версии.)
Предварительные требования
Для выполнения этого пошагового руководства читатель должен владеть основами языка C++.
Создание проекта и добавление исходного файла
Приведенные ниже инструкции немного отличаются в зависимости от используемой версии Visual Studio. Чтобы ознакомиться с документацией по предпочтительной версии Visual Studio, используйте селектор Версия. Он находится в верхней части оглавления на этой странице.
Создание проекта C++ в Visual Studio
- В главном меню выберите Файл >Создать >Проект, чтобы открыть диалоговое окно Создание проекта.
- В верхней части диалогового окна задайте для параметра Язык значение C++, для параметра Платформа значение Windows, а для Типа проекта — Консоль.
- В отфильтрованном списке типов проектов щелкните Консольное приложение, а затем нажмите кнопку Далее. На следующей странице введите имя проекта и при необходимости укажите расположение проекта.
- Нажмите кнопку Создать, чтобы создать проект.
Создание проекта C++ в Visual Studio 2017
- Создайте проект, наведите указатель на пункт Создать в меню Файл и выберите Пункт Проект.
- В области Типы проектов Visual C++ щелкните Рабочий стол Windows, а затем — Консольное приложение Windows.
- Введите имя проекта. По умолчанию решение, содержащее проект, имеет то же имя, что и проект, но вы можете ввести другое имя. Вы также можете ввести другое расположение для проекта.
- Чтобы создать проект, нажмите кнопку ОК .
Создание проекта C++ в Visual Studio 2015
- Создайте проект, наведите указатель на пункт Создать в меню Файл и выберите Пункт Проект.
- В области Типы проектов Visual C++ щелкните Рабочий стол Windows, а затем — Консольное приложение Windows.
- В диалоговом окне Новый проект разверните узел Установленные>шаблоны>Visual C++, а затем выберите Win32. В центральной области выберите Консольное приложение Win32.
- Введите имя проекта. По умолчанию решение, содержащее проект, имеет то же имя, что и проект, но вы можете ввести другое имя. Вы также можете ввести другое расположение для проекта.
- Чтобы создать проект, нажмите кнопку ОК .
- Завершите работу мастера приложений Win32.
- Нажмите кнопку Далее, а затем убедитесь, что консольное приложение выбрано, и снимите флажок Предкомпилированные заголовки .
- Нажмите кнопку Готово.
Добавление нового исходного файла
- Если Обозреватель решений не отображается, в меню Вид выберите Обозреватель решений.
- Добавьте в проект новый исходный файл, как показано ниже.
- В Обозреватель решений щелкните правой кнопкой мыши папку Исходные файлы, наведите указатель на пункт Добавить и выберите пункт Создать элемент.
- В узле Код щелкните Файл C++ (CPP), введите имя файла и нажмите кнопку Добавить.
CPP-файл отображается в папке Исходные файлыв Обозреватель решений, а файл открывается в редакторе Visual Studio.
Как скомпилировать CPP в EXE
wikiHow работает по принципу вики, а это значит, что многие наши статьи написаны несколькими авторами. При создании этой статьи над ее редактированием и улучшением работали, в том числе анонимно, 18 человек(а).
Количество просмотров этой статьи: 68 931.
Это пошаговое руководство по компиляции исходного кода на C++ в EXE файлы (в Windows). Описанные методы также работают при компиляции форматов .c ++, .cc и .cxx (возможно, что и .c). Это руководство предполагает, что исходный код на C++ является консольным приложением и не требует никаких внешних библиотек.
Скачайте C++ компилятор. Одним из лучших компиляторов для компьютеров под управлением Windows является бесплатный Microsoft Visual C++ 2012 Express.
Начните новый проект в Visual C++. Это довольно просто. Нажмите «New Project» в левом верхнем углу, а затем следуйте инструкциям на экране. Введите название проекта и в открывшемся окне нажмите «Finish».
Скопируйте и вставьте все .cpp файлы в каталог «Source Files», а затем скопируйте и вставьте все .h файлы (если таковые имеются) в каталог «Header Files». Переименуйте основной CPP файл (тот, который содержит «int main()») на выбранное вами название проекта.
Найдите EXE файл. Перейдите в каталог «Projects», в который Visual C++ помещает все программы (в Windows 7 этот каталог расположен в папке «Мои документы). В каталоге «Debug» найдите EXE файл под именем, которые вы дали ему ранее.
Проверьте файл. Дважды щелкните по EXE файлу, чтобы запустить его; если все прошло хорошо, то программа работает нормально. Если что-то пошло не так, попробуйте выполнить описанные действия еще раз.