double arrow

Область действия внешних идентификаторов


Если функция ссылается на идентификатор, описанный какextern, то где-то среди файлов или библиотек, образующихполную программу, должно содержаться внешнее определениеэтого идентификатора. Все функции данной программы, которыессылаются на один и тот же внешний идентификатор, ссылаютсяна один и тот же объект, так что следует позаботиться, чтобыспецифицированные в этом определении тип и размер были сов-местимы с типом и размером, указываемыми в каждой функции,которая ссылается на эти данные. Появление ключевого слова extern во внешнем определенииуказывает на то, что память для описанных в нем идентифика-торов будет выделена в другом файле. Следовательно, в состо-ящей из многих файлов программе внешнее определение иденти-фикатора, не содержащее спецификатора extern, должно появ-ляться только в одном из этих файлов. Любые другие файлы,которые желают дать внешнее определение этого идентифика-тора, должны включать в это определение слово extern. Иден-тификатор может быть инициализирован только в том описании,которое приводит к выделению памяти. Из этого правила в ОС ДЕМОС имеется исключение. Внешнийобъект может присутствовать в нескольких описаниях безextern. При этом длина объекта в разных описаниях должнасовпадать, а инициализация, если она есть, должна прово-диться ровно в одном из описаний. При нарушении этих правилбудет выдана ошибка на этапе редактировании связей прог-раммы. Идентификаторы, внешнее определение которых начинаетсясо слова static, недоступны из других файлов. Функции могутбыть описаны как static.

Неявные описания

Не всегда необходимо специфицировать и класс памяти итип идентификатора в описании. Во внешних определениях иописаниях формальных параметров и членов структур класспамяти определяется по контексту. Если в находящемся внутрифункции описании не указан тип, а только класс памяти, топредполагается, что идентификатор имеет тип int; если неуказан класс памяти, а только тип, то идентификатор предпо-лагается описанным как auto. Исключение из последнего пра-вила дается для функций, потому что спецификатор auto дляфункций является бессмысленным (язык Си не в состоянии ком-пилировать программу в стек); если идентификатор имеет типфункция, возвращающая ..., то он предполагается неявно опи-санным как extern. Входящий в выражение и неописанный ранее идентификатор,за которым следует скобка (, считается описанным по -42- контексту как функция, возвращающая int. /* extern */ int tab[100]; static /* int */ t1; /* int */ func(i) /* int i; */ { register /* int */ k; /* auto */ char buf[512]; /* extern int f1(); */ ... f1(a,b) ...

* 9. ПРЕПРОЦЕССОР ЯЗЫКА 'СИ'

Компилятор языка Си содержит препроцессор, который поз-воляет осуществлять макроподстановки, условную компиляцию ивключение именованных файлов. Строки, начинающиеся с #,являются командами этого препроцессорa. Синтаксис этих строкне связан с остальным языком; они могут появляться в любомместе и их влияние распространяется (независимо от областидействия) до конца исходного программного файла. Фактическипрепроцессор расширяет возможности языка Си, реализуя такиефункции, которые в других языках входят в состав самогоязыка (например, параметрические константы в Фортране-77).

Замена лексем

Команда #define идентификатор строка_лексем (обратите внимание на отсутствие в конце точки с запятой)приводит к тому, что препроцессор заменяет последующие вхож-дения этого идентификатора на указанную строку лексем.Строка вида #define идентификатор(идентифика- тор,...,идентификатор) строка_лексем где между первым идентификатором и открывающейся скобкой "("нет пробела, представляет собой макроопределение с аргумен-тами. В дальнейшем первый идентификатор, за которым следуетоткрывающая скобка "(", последовательность разделенных запя-тыми лексем и закрывающая скобка ")", заменяются строкойлексем из определения. Каждое вхождение идентификатора, упо-мянутого в списке формальных параметров в определении, заме-няется соответствующей строкой лексем из обращения. Факти-ческими аргументами в обращении являются строки лексем, раз-деленные запятыми; однако запятые, входящие в закавыченныестроки или заключенные в круглые скобки, не разделяют аргу-ментов. Количество формальных и фактических параметровдолжно совпадать. Текст внутри строки или символьной конс-танты не подлежит замене. -43- В обоих случаях замененная строка просматривается сновас целью обнаружения других идентификаторов, известных преп-роцессору. В обоих случаях слишком длинная строка определе-ния может быть продолжена на другой строке, если поместить вконце продолжаемой строки обратную косую черту "\". Описываемая возможность особенно полезна для определе-ния "объявляемых констант", как, например, #define TABSIZE 100 int table[TABSIZE]; или для замены некоторых функций с помощью макроподстановки: #define max(a,b) ((a)>(b)?(a):(b)) x = max(y,20) (в последнем определении a и b взяты в скобки, для того,чтобы фактическими параметрами макро могли бы быть произ-вольные выражения. Команда #undef идентификатор приводит к отмене препроцессорного определения данного иден-тификатора. Определить идентификатор можно не только с помощьюкоманды #define, но также и при вызове компилятора, спомощью параметров команды cc.

Включение файлов

Команда #include "filename" приводит к замене этой строки на все содержимое файла с име-нем filename. Файл с этим именем сначала ищется в текущемсправочнике, а затем в других "стандартных" местах, опреде-ляемых пользователем при вызове компилятора. В отличие отэтого команда #include <filename> ищет файл только в стандартном справочнике системы. В ОС ДЕМОС файл ищется в справочнике /usr/include. Команды #include могут быть вложенными. -44-

Условная компиляция

Команда препроцессора #if константное выражение проверяет, отлично ли от нуля значение константного выраже-ния. Команда: #ifdef идентификатор проверяет, определен ли этот идентификатор в настоящиймомент в препроцессоре, т.е. определен ли этот идентификаторс помощью команды #define. Команда: #ifndef идентификатор проверяет, является ли этот идентификатор в данный момент неопределенным для препроцессора. За каждым из трех перечисленных видов строк может сле-довать произвольное число строк, возможно содержащих командупрепроцессора #else а затем должна следовать команда: #endif Если проверяемое условие истинно, то любые строки между#else и #endif игнорируются. Если проверяемое условие ложно,то любые строки между проверяемой строкой и #else или, приотсутствии #else, #endif игнорируются. Эти конструкции могут быть вложенными. Например: #ifdef DEBUG fprintf(stderr,"i=%o j=%d\n",i,j); #endif Переменная препроцессора может быть определена не только всамой программе, но и при вызове транслятора.

Команда #line

Для других препроцессоров, генерирующих Си-программы,полезна следующая команда: #line константа "имя_файла" -45- которая сообщает компилятору (для диагностических сообще-ний), что следующая строка исходного файла имеет номер,задаваемый константой, и что текущий входной файл именуетсяименем_файла. Если имя_файла отсутствует, то запоминаемоеимя файла не изменяется. Пример: #line 250 "gram.y"

* 10. ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ О ТИПАХ

В этом разделе обобщаются сведения об операциях, кото-рые можно применять только к объектам определенных типов.

Сейчас читают про: