Цель этой статьи — подробно представить полный процесс компиляции, с момента написания файла в исходном коде до получения исполняемого двоичного файла на языке C. Показанные команды и утилиты соответствуют системам GNU/Linux, но процесс одинаков для любой операционной системы и компилятора.

C — это язык среднего уровня, и ему нужен компилятор для преобразования его в исполняемый код, чтобы программу можно было запустить на нашей машине.

Компилятор — это компьютерная программа, которая помогает преобразовать исходный код, написанный на языке высокого уровня, в машинный язык низкого уровня. Он переводит код, написанный на одном языке программирования, на другой язык без изменения смысла кода. Компилятор также делает конечный код эффективным, оптимизированным для времени выполнения и объема памяти.

Этапы компиляции

Компиляция программы на языке C представляет собой многоэтапный процесс. На общем уровне процесс можно разделить на четыре отдельных этапа: предварительная обработка, компиляция, сборка и компоновка.

Как мы скомпилируем и запустим программу на C?

Ниже приведены шаги, которые мы используем на машине Ubuntu с компилятором gcc. Коллекция компиляторов GNU (GCC) — это оптимизирующий компилятор, созданный в рамках проекта GNU, поддерживающий различные языки программирования, аппаратные архитектуры и операционные системы. Фонд свободного программного обеспечения (FSF) распространяет GCC как бесплатное программное обеспечение в соответствии с Стандартной общественной лицензией GNU (GNU GPL).

Шаг 1 Препроцессор:

Первый этап компиляции называется предварительной обработкой. Проще говоря, препроцессор C — это просто инструмент подстановки текста, который указывает компилятору выполнить необходимую предварительную обработку перед фактической компиляцией.

Исходный файл состоит из нескольких заголовочных файлов, и эти файлы включаются в тело кода C препроцессором перед началом компиляции.

В случае gcc опция -E (с учетом регистра) используется для создания дампа единицы перевода исходного кода.

Затем скомпилируйте его, используя команду ниже.

$ gcc –E hello.c

Шаг 2. Компилятор:

Код, расширенный препроцессором, передается компилятору. Компилятор преобразует этот код в ассемблерный код. Или мы можем сказать, что компилятор C преобразует предварительно обработанный код в ассемблерный код.

Команда для выполнения этого шага:

$ gcc –c имя_файла.c

Шаг 3 Ассемблер:

Сборка преобразует написанную программу на языке ассемблера в объектный код, двоичный файл на языке машин, исполняемый процессором.

Команда для выполнения этого шага:

$ gcc –S hello.c

Этап 4 компоновщика:

После того, как компилятор создал все объектные файлы, вызывается другая программа, которая объединяет их в исполняемый программный файл. Эта программа называется компоновщиком, а процесс их объединения в исполняемый файл называется компоновкой.

Компоновщик просматривает все объектные файлы, которые вы ему сказали использовать.

Он собирает два списка,

— список общедоступных
элементов, найденных в объектных файлах. Это элементы, предоставляемые этими файлами

— список неразрешенных внешних элементов
Элементы, которые, по словам объектных файлов, им нужны, но отсутствуют

Затем он проверяет объектные файлы, находя паблики и помещая их адреса в неразрешенные внешние файлы, пока все потребности не будут удовлетворены. В этот момент у него есть все необходимое для создания двоичного файла, содержащего всю программу. Этот двоичный файл является исполняемым файлом.

На следующем изображении мы сгруппировали шаги, о которых мы говорили ранее:

Очень хорошо, теперь мы проследим этот процесс шаг за шагом, используя соответствующие команды.

Сделаем программу, которая выводит на экран «Hello Holberton»

Сначала мы создаем программу на C с помощью редактора и сохраняем файл как hello.c.

$ emacs hello.c

Мы подготовили наш код, сохранили и запустили процесс компиляции.

$ gcc –E hello.c

Мы подготовили наш код, сохранили и запустили процесс компиляции.

Затем мы выполняем команду для выполнения второго этапа:

$ gcc –c hello.c

И… это говорит нам, что у нас есть ошибка, поэтому мы перепроверяем наш код и исправляем ее, добавляя a; что нам не хватает в линии, которая там указывает на нас.

Повторяем шаги и теперь он работает без предупреждений. Обратите внимание, что теперь у нас есть новый файл с тем же именем и расширением .o. Если мы увидим содержимое этого нового файла, мы увидим что-то вроде следующего:

Давайте продолжим, теперь мы собираемся использовать следующую команду:

$ gcc –S hello.c

Теперь у нас есть новый файл с таким же именем и расширением .s. Если мы увидим содержимое этого нового файла, мы увидим что-то вроде следующего:

Ну а теперь мы собираемся создать исполняемый файл и для этого воспользуемся следующей командой:

$ gcc hello.c

Идеально! теперь у нас есть исполняемый файл с именем a.out, имя, которое назначается по умолчанию.

Хорошо, когда мы запустим его, мы увидим на экране Hello Holberton, так что наша программа готова! Вы также можете увидеть, как выглядит его содержимое, с помощью команды cat.

Итак, это процесс компиляции наших программ на C.

Спасибо за внимание. Мы будем читать друг друга в следующей возможности!