Назад |
Си-компилятор является основным средством для создания программ в системе UNIX. Переоценить его значение для системы невозможно. Достаточно сказать, что около 90% ядра и почти 100% утилит и библиотек системы UNIX написаны на языке Си.
В системе UNIX Си-компилятор, как правило, состоит из трех программ: препроцессора, синтаксического анализатора и генератора кода. Результатом его работы является программа на языке ассемблера, которая затем транслируется в объектный файл, компонуемый с другими модулями загрузчиком. В результате образуется выполняемая программа.
Как и большинство сложных программ в системе UNIX, компилятор состоит из нескольких выполняемых файлов. Основной из команд, с которой приходится иметь дело пользователю, является команда cc. Ее функцией является проанализировать введенную командную строку и выполнить действия, предписанные указанными после имени команды параметрами. Для выполнения описанной выше последовательности действий требуется вызвать программы:
/lib/cpp - препроцессор; /lib/cc0 - синтаксический анализатор; /lib/cc1 - генератор кода; /bin/as - ассемблер; /bin/ld - загрузчик.
По завершении каждой стадии компиляции создаются промежуточные временные файлы, используемые впоследствии. Исключением является синтаксический анализатор, который в случае обнаружения ошибок в исходном коде программы, выводит соответствующие сообщения в файл стандартного вывода ошибок. Заданием параметров в командной строке компилятора можно получить промежуточный результат после обработки программного модуля (файла) на каждой из фаз.
Следует отметить, что Си-компилятор зависит от версии системы. UNIX может иметь стандартный Си-компилятор, разработанный еще при создании первых версий ОС. Этот компилятор описан в [6] и в честь авторов его обычно называют "Керниган-Ритчи Си". Современные версии системы UNIX имеют в своем составе ANSI Си-компилятор.
Для получения выполняемого файла программы, исходный текст которой содержится в одном файле "myprog.c", достаточно ввести следующую строку:
cc myprog.c
Если в тексте программы не обнаруживается синтаксических ошибок и она не использует функций, не входящих в стандартную Си-библиотеку, в текущей директории будет создан файл "a.out", содержащий выполнимый модуль откомпилированной программы. Но для большинства программ нецелесообразно помещать всю программу в один единственный файл. Может также потребоваться использование дополнительных библиотек, добавление в выполняемый файл информации для отладчика, оптимизация выполняемого кода и многое другое. Сообщить компилятору о том, что и как требуется создать из исходных файлов, можно при помощи ключей, задаваемых в командной строке.
Синтаксис командной строки для Си-компилятора следующий:
cc [ключ [имя файла]] ...
Программа, написанная на языке Си, составляется из одной или более программных единиц, называемых функциями. Исходные тексты функций, составляющих программу, располагаются в одном или нескольких файлах. Компилятор служит для получения выполняемого модуля из файлов, содержащих исходные тексты программы. Си-компилятор работает только с файлами определенных типов. Тип задается именем файла, а точнее, его расширением. Эти расширения и соответствующие типы файлов следующие:
.c - исходные тексты программ;
.i - выходные файлы препроцессора;
.s - код на языке ассемблера;
.h - включаемые препроцессором файлы-заголовки;
.o - объектные файлы;
.a - архивы объектных файлов (библиотеки).
Заставить компилятор выполнять необходимые действия с заданными файлами можно при помощи ключей (опций). Опции, задающие режим работы компилятора, делятся на следующие группы:
Общее количество опций довольно велико. Все они описаны в справочном руководстве по системе (UNIX User Manual). Мы же ограничимся описанием наиболее часто, с нашей точки используемых ключей.
Опция '-c' сообщает компилятору, что входные файлы должны быть откомпилированы или ассемблированы, но не объединены в выполнимую программу. Полученные объектные модули по умолчанию помещаются в файлы с именами, полученными заменой расширений ".c", ".i" или ".s" на ".o". Так команда
cc -c myprog.c
порождает объектный файл "myprog.o".
По умолчанию компилятор создает выполнимый файл с именем "a.out". Изменить это имя можно с помощью ключа '-o'. Так, в результате работы команды
cc -o myprog myprog.c
из файла "myprog.c" будет создан выполнимый модуль с именем myprog.
Оптимизировать объектный код можно, указав в командной строке ключ '-O'. Некоторые реализации Си-компилятора поддерживают несколько уровней (степеней) оптимизации генерируемого объектного кода. Для этого используется ключ '-On', где n - число, задающее уровень (например, '-01' или '-02').
Для отладки программы при помощи символьного отладчика загрузочный модуль (файл) должен содержать соответствующие данные. Сгенерировать объектный код, содержащий их, можно при помощи ключа '-g'.
Ключ '-D' позволяет задать директиву препроцессора "#define" без изменения исходного текста программы. Наличие в командной строке записи "-Dname=value" эквивалентно указанию в файле с исходным кодом строки:
#define name value
Если поле value в командной строке отсутствует, константе присваивается значение 1. Задание знаачений в командной строке имеет приоритет перед определением их в тексте программы.
Одним из важнейших свойств языка а программирования Си является использование включаемых файлов (или файлов-заголовков). Имена их, как правило, заканчиваются расширением ".h". Как мы уже упоминали, стандартные файлы-заголовки в системе UNIX располагаются в директории "/usr/include". Если имя включаемого файла-заголовка в тексте программы указывается в скобках ("<...>"), и имя файла не начинается с символа '/', препроцессор составляет полное имя файла по правилу: "/usr/include" + "имя файла". Если включаемый файл находится в директории, не являющейся поддиректорией "/usr/include", можно указать его местоположение при помощи ключа "-I". Например, если есть необходимость использовать файл-заголовок, находящийся в директории "/work/include", нужно указать его местоположение следующим образом:
cc -I/work/include -c myprog.c
Следует заметить, что важна последовательность указанных в командной строке директорий при поиске соответствующих файлов. Если файлы с одинаковым именем находятся в разных директориях, эти директории указаны в командной строке с ключом "-I", то компилятор выберет файл из директории, указанной ранее.
Для указания загрузчику того, что при создании выполняемого файла требуется использовать какой-либо библиотечный файл, используется ключ "-l". Мы уже говорили, что библиотеки объектных модулей хранятся в файлах, имена которых начинаются с "lib" и заканчиваются расширением ".а". Часть имени файла между ними является собственно именем библиотеки. В системе UNIX все стандартные системные библиотеки располагаются в директории "/usr/lib". Стандартная Си-библиотека системы располагается в файле "/usr/lib/libc.a" и подключается загрузчиком автоматически без указания ее в командной строке. Все остальные библиотеки являются дополнительными и требуют явного указания для использования. Например, если в программу надо включить библиотеку математических функций, находящуюся в файле "/lib/libm.a", командная строка должна содержать ссылку на нее в виде "-lm":
cc -o myprog myprog.c -lm
Если необходимая библиотека находится в директории, отличной от стандартных ("lib" и "/usr/lib"), необходимо указать ее расположение при помощи ключа "-L". Действие ключа "-L" и правила поиска архивных файлов те же, что и при поиске файлов-заголовков с использованием ключа "-I". Например:
cc -L. . /lib -o myprog myprog.c -lmyprog
Здесь при компоновке программы будет подключаться архив с именем "libmyprog.a", находящийся в директории "../lib".