Новичку команда gcc кажется магией. В одном конце идет файл с каким-то кодом на C, а в другом выплевывается исполняемый файл. Лучший способ демистифицировать это — изучить четыре этапа компиляции: предварительная обработка, компиляция, сборка и компоновка.

При предварительной обработке компилятор переводит директивы препроцессора в объекты, которые они представляют. Директивы препроцессора — это строки в верхней части файлов, которые начинаются с #. Например, предположим, что у нас есть следующий файл:

#include <stdio.h> включает код из заголовочного файла stdio.h в исходный код этой программы. #define EXAMPLE “example\n” заменяет токен EXAMPLE, встречающийся в любом месте кода, на example\n. Мы можем увидеть это, если остановим процесс компиляции после предварительной обработки, используя опцию -E. Заголовочные файлы обычно содержат такие директивы.

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

Ассемблерный код читается людьми, но писать его было бы неинтересно. Мы можем увидеть ассемблерный код, если остановим процесс компиляции после компиляции, используя опцию -S.

При ассемблере компилятор переводит ассемблерный код в машинный код. Машинный код выполняется непосредственно процессором. Чтобы остановить компиляцию после сборки, используйте опцию -c. Мы можем увидеть машинный код в двоичном виде, используя команду od с опцией -c.

При компоновке компилятор берет файл или файлы машинного кода и упорядочивает их в исполняемый файл. Чтобы увидеть результат этого, мы можем просто запустить gcc без каких-либо параметров, поскольку связывание — это последняя часть процесса. Имя файла по умолчанию для исполняемого файла — a.out, но можно использовать параметр -o, чтобы дать ему другое имя. Чтобы увидеть результат компиляции, введите ./, а затем имя файла.

Это обзор компиляции, поэтому многие нюансы выходят за рамки данной статьи. Для некоторых связанных концепций взгляните на эти страницы Википедии:







Удачного кодирования!