Смекни!
smekni.com

Препроцессор языка C. (стр. 2 из 10)

имя файла, содержащего три символа backslash.

'#include ANYTHING ELSE'

Эта модификация называется "вычисляемой директивой #include". Любая

директива '#include', не соответствующая ни одной из модификаций,

рассмотреных выше, является вычисляемой директивой. Строка ANYTHING ELSE

проверяется на наличие соответствующего макроса, значение которого затем

заменяет его название. Полученная в результате строка должна уже

в точности соответствовать одной из рассмотренных выше модификаций (то есть

имя подключаемого файла должно быть заключено в кавычки или угловые скобки).

Эта возможность позволяет определять макросы, что дает возможность

изменять имена подключаемых файлов. Эта возможность, например, используется

при переносе программ с одной операционной системы на другие, где требуются

разные подключаемые файлы.

3.3. Как работает директива '#include'

Директива '#include' указывает С препроцессору обработать указанный

файл перед обработкой оставшейся части текущего файла. Информация, выдаваемая

препроцессором, содержит уже полученные данные, за которыми следуют данные,

получаемые при обработке подключаемого файла, а за которыми, в свою очередь,

следуют данные, получаемые при обработке текста, следующего после директивы

'#include'. Например, дан следующий подключаемый файл 'header.h':

char *test ();

и основная программа с именем 'program.c', использующая этот файл.

int x;

#include "header.h"

main ()

{

printf (test ());

}

Данные, полученные при обработке программы 'program.c' будут выглядеть

следующим образом:

int x;

char *test ();

main ()

{

printf (test ());

}

Для подключаемых файлов нет ограничений на объявления и

макроопределения. Любой фрагмент С программы может быть включен в другой

файл. Подключаемый файл может даже содержать начало выражения,

заканчивающееся в исходном файле или окончание выражения, начало которого

находится в исходном файле. Хотя комметарии и строковые константы не могут

начинаться подключаемом файле и продолжаться в исходном файле. Не завершенный

комментарий, стороковая или символьная константа в подключаемом файле

приводят к возникновению ошибки в конце файла.

Подключаемый файл может содержать начало или окончание сиснтаксической

конструкции, такой как определение функции.

Срока, следующая за директивой '#include' всегда является пустой и

добавляется С препроцессором даже если подключаемый файл не содержит

завершающий символ перевода строки.

3.4. Однократно подключаемые файлы

Часто случается, что подключаемый файл включает в себя другой файл. Это

может привести к тому, что отдельный файл будет подключаться неоднократно,

что может привести к возникновению ошибок, если файл определяет типы

структур или определения типов. Поэтому следует избегать многократного

подключения файлов.

Обычно это достигается путем заключения в условие всего содержимого

этого файла, как показано ниже:

#ifndef FILE_FOO_SEEN

#define FILE_FOO_SEEN

Сам файл

#endif /* FILE_FOO_SEEN */

Макрос 'FILE_FOO_SEEN' указывает на то, что файл уже однажды вкючался.

В подключаемых файлах пользователя макрос не должен начинаться с символа

'_'. В системных подключаемых файлах его имя не должно начинаться с символа

'__' во избежание возникновения конфликтов с программами пользователя. Каким

бы ни был файл, имя макроса должно содержать имя файла и некоторый

дополнительный текст во избежание вознкновения конфликтов с другими

подключаемыми файлами.

Препроцессор GNU C построен таким образом, что обработке подключаемого

файла он проверяет наличие определенных конструкций и наиболее рационально

их обрабатывает. Препроцессор специально отмечает полное вложение файла в

условие '#ifndef'. Если в подключаемом файле содержится директива '#include',

указывающая на обрабатываемый файл, или макрос в директиве '#ifndef' уже

определен, то обрабатываемый файл полностью игнорируется.

Существует также специальная директива, указывающая препроцессору, что

файл должен быть включен не более одного раза. Эта директива называется

'#pragma once'. Она использовалась в дополнение к директиве '#ifndef' и

в настоящее время она устарела и не должна прменяться.

В объектно ориентированном языке С существует модификация директивы

'#include', называемая '#import', которая используется для вкючения файла

не более одного раза. При использовании директивы '#import' вместо

'#include' не требуется наличия условных оборотов для предотвращения

многократной обработки файла.

3.5. Подключаемые файлы и наследование

"Наследование" это то, что происходит, когда какой либо объект или

файл образует некоторую часть своего содержимого путем виртуального

копирования из другого объекта или файла. В случае подключаемых С файлов

наследование означает, что один файл включает другой файл, а затем заменяет

или добавляет что-либо.

Если наследуемый подключаемый файл и основной подключаемый файл имеют

различные имена, то такое наследование называется прямым. При этом

используется конструкция '#include "BASE"' в наследуемом файле.

Иногда необходимо чтобы у наследуемого и основного подключаемого файла

были одинаковые имена.

Например, предположим, что прикладная программа использует системный

подключаемый файл 'sys/signal.h', но версия файла '/usr/include/sys/signal.h'

на данной системе выполняет того, что требуется в прикладной программе.

Будет удобнее определить локальную версию, возможно с именем

'/usr/local/include/sys/signal.h' для замены или добавления к версии,

поставляемой с системой.

Это можно выполнить с применением опции '-I.' при компиляции, а также

созданием файла 'sys/signal.h' который выполняет требуемые программе функции.

Но сделать так, чтобы этот файл включал стандартный файл 'sys/signal.h' не

так просто. При включении строки '#include <sys/signal.h>' в этот файл

произойдет подключение новой версии файла, а не стандартной системной версии.

Это приведет к рекурсии и ошибке при компиляции.

При использовании директивы `#include </usr/include/sys/signal.h>'

нужный файл будет найден, но этот способ является не эфективным, так как

содержит полный путь к системному файлу. Это может отразиться на содержании

системы, так как это означает, что любые изменения местоположения системных

файлов потребуют дополнительных изменений где-либо еще.

Более эффективным решением этой проблемы является применение директивы

'#include_next', которая используется для подключения следующего файла с

таким же именем. Эта директива функционирует также как и директива '#include'

за исключением поиска требуемого файла. Она начинает поиск списка каталогов

подключаемых файлов после каталога, где был найден текущий файл.

Предположим была указана опция '-I /usr/local/include', а список

каталогов для поиска включает '/usr/include'. Также предположим, что оба

каталога содержат файл с именем 'sys/signal.h'. Директива

'#include <sys/signal.h>' найдет нужный файл под каталогом

'/usr/local/include'. Если этот файл содержит строку

'#include_next <sys/signal.h>', то поиск будет возобновлен после предыдущего

каталога и будет найден файл в каталоге '/usr/include'.

4. Макросы

Макрос это тип сокращения, который можно заранее определить и

использовать в дальнейшем. Существует довольно много возможностей, связанных

с использованием макросов в С препроцессоре.

4.1. Простые макросы

"Простой макрос" это тип сокращения. Это идентификатор, который

используется для представления фрагмента кода.

Перед использованием макроса его необходимо определить с помощью

директивы '#define', за которой следует название макроса и фрагмент кода,

который будет идентифицировать этот макрос. Например,

#define BUFFER_SIZE 1020

определяет макрос с именем 'BUFFER_SIZE', которому соответствует текст

'1024'. Если где-либо после этой директивы встретится выражение в следующей

форме:

foo = (char *) xmalloc (BUFFER_SIZE);

то С препроцессор определит и заменит макрос 'BUFFER_SIZE' на его значение и

в результате получится

foo = (char *) xmalloc (1020);

Использование прописных букв в названиях макросов является стандартным

соглашением и повышает читабельность программ.

Обычно, макроопределением должна быть отдельная строка, как и при

использовании всех директив препроцессора. (Длинное макроопределение можно

разбить на несколько строк с применением последовательности

backslash-newline.) Хотя существует одно исключение: символы перевода

строки могут быть вкючены в макроопределение если они находятся в строковой

или символьной константе, потому как макроопределение не может содержать

каких-либо специальных символов. Макроопределение автоматически дополняется

соответствующим специальным символом, который завершает строчную или

символьную константу. Комментарии в макроопределениях могут содержать символы

перевода строки, так как это ни на что не влияет, потому как все комментарии

полностью заменяются пробелами вне зависимости от того, что они содержат.

В отличие от выше сказанного, не существует никаких ограничений на

значение макроса. Скобки не обязательно должны закрываться. Тело макроса не

обязательно должно содержать правильный С код.

Препроцессор С обрабатывает программу последовательно, поэтому

макроопределения вступают в силу только в местах, где они используются.

Поэтому, после обработки следующих данных С препроцессором

foo = X;

#define X 4

bar = X;

получится такой результат

foo = X;

bar = 4;

После подстановки препроцессором имени макроса, тело макроопределения