Назад

Содержание

Вперед


1.9.3. Компилятор языка Си.

    Си-компилятор является основным средством для создания программ в системе 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".