Зачем мы компилируем наш код?

Процесс c-компиляции — это способ преобразования нашего исходного кода в машинный код, который компьютер воспринимает как входные данные. Как мы знаем, единственный способ связи с компьютером — это двоичная система (нули и единицы), которые представляют два состояния электричества: включено (1) и выключено (0).

Чтобы перейти к двоичной системе, которую понимает наш компьютер, используется компилятор gcc, происходящий от аббревиатуры Коллекция компиляторов GNU. Это оптимизирующий компилятор, созданный проектом GNU, поддерживающим различные языки программирования, аппаратные архитектуры и операционные системы

Как компилятор gcc преобразует мой код в машинный код?

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

Каковы этапы компиляции?

Шагов компиляции 4:

  • Препроцессор
  • Компилятор
  • Ассемблер
  • Линкер

Теперь будет подробно из чего состоит каждый шаг компиляции:

1. Препроцессор

Первое, что делает процесс компиляции c, — это проходит через препроцессор. Исходный код написан в текстовом редакторе, и этот файл имеет расширение «.c».

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

Разверните найденные макросы и разверните включенные файлы. Например, если в программе имеется директива ‹stdio.h›, то препроцессор интерпретирует директиву и заменяет ее содержимым stdio.h.

То, что мы получаем после прохождения препроцессора, представляет собой расширенный код, чтобы остановить процесс после шага препроцессора, мы используем команду gcc -E main.c.

Принимая во внимание, что main.c — это имя файла, который мы компилируем, а его содержимое следующее:

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

$gcc -E main.c -o c

2. Компилятор

На этом этапе код, расширенный препроцессором, преобразуется компилятором в ассемблерный код. Точно так же, чтобы визуализировать изменение нашего исходного кода с помощью команды -S, мы можем остановить процесс компиляции после прохождения через компилятор и до достижения ассемблера.

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

$gcc -S main.c

3. Ассемблер

На этом этапе ассемблер преобразует ассемблерный код в объектный код. Чтобы иметь возможность увидеть результат нашего исходного кода после прохождения ассемблера, мы используем команду -c, и это по умолчанию создает файл main.o, который при отображении с помощью команды cat мы можем увидеть его содержимое, как показано на следующее изображение.

$gcc main.c -c

4. Линкер

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

Чтобы проверить этот последний шаг, мы можем использовать команду $size main.o и $size main, последняя является конечным результатом всего процесса компиляции, в котором она становится исполняемым файлом.

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

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

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

Спасибо за внимание.