C - это компилируемый язык. Его исходный код пишется с помощью любого редактора по выбору программиста в виде текстового файла, а затем он должен быть скомпилирован в машинный код.
Исходные файлы C по соглашению называются с расширением .c, и мы используем команду «gcc» для компиляции исходных файлов C. (GCC - это аббревиатура от GNU Compiler Collection, и это система компиляции, созданная проектом GNU.)
Четыре этапа компиляции: предварительная обработка, компиляция, сборка, компоновка.
Предварительная обработка:
Предварительная обработка - это первый шаг. Препроцессор подчиняется командам, начинающимся с символа # (так называемые директивы):
- удаление комментариев
- расширяющиеся макросы
- расширение включенных файлов
Если вы включили файл заголовка, например #include ‹stdio.h›, он будет искать файл stdio.h и копировать файл заголовка в файл исходного кода.
Препроцессор также генерирует код макроса и заменяет символические константы, определенные с помощью #define, их значениями.
Компиляция:
Компиляция - второй шаг. Он принимает выходные данные препроцессора и генерирует язык ассемблера, промежуточный читаемый человеком язык, специфичный для целевого процессора.
Сборка:
Сборка - это третий этап компиляции. Ассемблер преобразует ассемблерный код в чистый двоичный код или машинный код (нули и единицы). Этот код также известен как объектный код.
Ссылка:
Связывание - это последний этап компиляции. Компоновщик объединяет весь объектный код из нескольких модулей в один. Если мы используем функцию из библиотек, компоновщик свяжет наш код с кодом этой библиотечной функции.
При статической компоновке компоновщик копирует все используемые библиотечные функции в исполняемый файл. При динамической компоновке код не копируется, это делается просто путем помещения имени библиотеки в двоичный файл.
Теперь приступим к компиляции! В нашем примере мы будем использовать main.c в качестве исходного файла.
#include <stdio.h> int main(void) { printf("Hello, World!\n"); return (0); }
В командной строке введите команду «gcc main.c» и нажмите Enter. Если он успешно компилируется, снова отобразится приглашение оболочки. Если он не компилируется, будет отображаться сообщение (я) об ошибке.
vagrant@vagrant-ubuntu-trusty-64:~$ gcc main.c vagrant@vagrant-ubuntu-trusty-64:~$ ls a.out main.c
После компиляции main.c введите команду «ls», чтобы вывести список содержимого вашего каталога, и вы увидите исполняемый файл с именем a.out. Чтобы запустить программу, введите «./a.out» в командной строке и нажмите Enter. Ура, мы видим правильный ответ «Hello, World!» за которым следует новая строка.
vagrant@vagrant-ubuntu-trusty-64:~$ ./a.out Hello, World! vagrant@vagrant-ubuntu-trusty-64:~$
Если вы не хотите, чтобы ваш выходной файл назывался a.out, которое является именем выходного файла по умолчанию, вы можете указать другое имя выходного файла с параметром -o.
gcc -o <desired_output_filename> <source filename>
Давайте посмотрим на пример ниже, где мы хотим, чтобы выходной файл назывался main.
vagrant@vagrant-ubuntu-trusty-64:~$ gcc -o main main.c vagrant@vagrant-ubuntu-trusty-64:~$ ls main main.c vagrant@vagrant-ubuntu-trusty-64:~$
Чтобы запустить основную программу, мы набираем «./main» в терминал.
vagrant@vagrant-ubuntu-trusty-64:~$ ./main Hello, World! vagrant@vagrant-ubuntu-trusty-64:~$
Если вы внесете изменения в свой код (т. е. внесете какие-либо изменения в исходный файл), вам потребуется сохранить и перекомпилировать.
В некоторых случаях исходный файл C не может быть успешно скомпилирован.
Прочтите сообщения об ошибках, чтобы узнать, как их исправить.
Ниже мы опустили точку с запятой в конце оператора printf.
#include <stdio.h> int main(void) { printf("Hello, World!\n") return (0); }
В сообщении об ошибке отображается, что перед возвратом должна была стоять точка с запятой.
vagrant@vagrant-ubuntu-trusty-64:~$ gcc -o main main.c main.c: In function 'main': main.c:5:7: error: expected ';' before 'return' return (0); ^ vagrant@vagrant-ubuntu-trusty-64:~$
Если мы снова добавим точку с запятой…
#include <stdio.h> int main(void) { printf("Hello, World!\n"); return (0); }
… А затем перекомпилируйте, сообщение об ошибке исчезнет.
vagrant@vagrant-ubuntu-trusty-64:~$ gcc -o main main.c vagrant@vagrant-ubuntu-trusty-64:~$ ./main Hello, World! vagrant@vagrant-ubuntu-trusty-64:~$
Даже если исходный файл компилируется, проверьте правильность вывода.
Вот пример исходного файла, который компилирует…
#include <stdio.h> int main(void) { printf("The sum of 9+2 is: %i\n", 10); return (0); }
… Но вывод неверен.
vagrant@vagrant-ubuntu-trusty-64:~$ ./main2 The sum of 9+2 is: 10 vagrant@vagrant-ubuntu-trusty-64:~$
К сожалению, у нас есть ошибка из-за опечатки в исходном коде.
printf("The sum of 9+2 is: %i\n", 10);
Мы знаем, что 9 + 2 = 11, но компилятор выполнил код, указанный в приведенной выше строке. Он подставил число 10 в спецификатор формата% i.
Правильный код был бы:
#include <stdio.h> int main(void) { printf("The sum of 9+2 is: %i\n", 11); return (0); }
Подводя итог, можно сказать, что четыре этапа компиляции: предварительная обработка, компиляция, сборка, компоновка.
Прочтите справочную страницу gcc для получения дополнительной информации. Удачного кодирования!