Процесс компиляции включает четыре последовательных этапа: предварительная обработка, компиляция, сборка и связывание. Чтобы перейти от написанной человеком исходной программы к исполняемому файлу, необходимо последовательно выполнить эти четыре этапа. Команды gcc и g++ могут выполнять весь процесс сразу, в этом мы будем использовать gcc, и четыре этапа:

Предварительно обработано:
На этом этапе директивы интерпретируются препроцессору. Среди прочего, переменные, инициализированные с помощью #define, заменяются в коде их значением во всех местах, где встречается их имя.
В качестве примера мы будем использовать эту простую программу:

#define PI 3.1416
main()
{
  float area, radio;
radius 10;
  area - PI * (radio * radio);
  printf("Circle.");
  printf("%s%f-n-n", "Circle area radius 10: ", area);
}

Предварительную обработку можно заказать с помощью любой из следующих команд; cpp конкретно относится к препроцессору.

 $ gcc -E circle.c > circle.pp
 $ cpp circle.c > circle.pp

Если мы изучим круг.pp, то увидим, что переменная PI была заменена ее значением 3,1416, как указано в решении #define.

Компиляция:

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

$ gcc -S circle.c

Сборка:

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

$ as -o circle.o circle.s

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

$ gcc -c circulo.c

Это создает файл circle.o из circle.c.
В больших программах, где многие исходные файлы написаны в коде C, очень часто используется gcc или g++ с параметром -c для компиляции каждого исходного файла отдельно. , а затем привязать все созданные объектные модули. Эти операции автоматизированы путем помещения их в файл с именем make file, интерпретируемый командой make, которая заботится о необходимых минимальных обновлениях всякий раз, когда какая-либо часть кода изменяется в любом из исходных файлов.

4. Linked
Последний этап — собрать один или несколько модулей в объектном коде с кодом, существующим в библиотеках.
Для привязки используем команду ld, для получения исполняемого файла используем что-то вроде этого:

$ ld -o cirle /usr/lib/gcc-lib/i386-linux/2.95.2/collect2 -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o circle /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc-lib/i386-linux/ 2.95.2/crtbegin.o -L/usr/lib/gcc-lib/i386-linux/2.95.2 circle.o -lgcc -lc -lgcc /usr/lib/gcc-lib/i386-linux/2.95.2/crtend.o /usr/lib/crtn.o

Как сделать все за один шаг?

В программе с одним исходным файлом весь описанный выше процесс можно выполнить за один шаг:

$ gcc -o circle circle.c

Файл circle.o не создается; промежуточный объектный код создается и уничтожается незаметно для оператора, но исполняемая программа появляется там и работает.
Поучительно использовать опцию -v gcc для получения подробного отчета обо всех шагах сборки:

$ gcc -v -o circle.c