The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]

Каталог документации / Раздел "Программирование, языки" / Оглавление документа
Вперед Назад Содержание

8. Директивы препроцессора и Прагмы

8.1 Введение

Директивы предпроцессора это инструкции предпроцессору С. Предпроцессор С это текстовый процессор, который манипулирует текстом исходного файла на первой фазе компиляции. Хотя компилятор вызывает предпроцессор на своей первой стадии, его можно вызвать и отдельно для обработки текста без компилирования.

Директивы предпроцессора обычно используются для облегчения внесения изменений в исходные программы и для облегчения их компилирования в разных средах выполнения. Расположенные в исходном файле директивы заставляют предпроцессор выполнять конкретные действия. Например, предпроцессор может заменить лексемы в тексте, вставить содержимое других файлов в исходный файл или подавить компиляцию части файла, удаляя сегменты текста.

Предпроцессор С распознает следующие директивы:

           #define        #if            #line   
           #elif          #ifdef         #undef   
           #else          #ifndef   
           #endif         #include   
Знак номера (#) должен быть первым неразделительным символом на строке, содержащей директиву, между символом номера и первой буквой директивы могут появляться разделительные символы. Некоторые директивы содержат аргументы или значения. Любой текст, который следует за директивой (кроме аргумента или значения, который является частью директивы) должен быть заключен в скобки комментария (/* */).

Директивы предпроцессора могут появляться в произвольном месте исходного файла, но они будут воздействовать только на оставшуюся часть исходного файла, в котором они появились.

"Оператор предпроцессора" это оператор, который признается оператором только в контексте директив предпроцессора. Есть только три специфические оператора предпроцессора: "строковый" (#), "вставки лексем" (##) и defined. Первые два оператора будут рассмотрены в данной Главе позднее в контексте директивы #define. Оператор defined также будет позже рассмотрен в данной Главе в Разделе "Директивы #if, #elif, #else и #endif".

"Прагма" это "прагматическая", или практическая, инструкция компилятору С. Прагмы в исходном файле С обычно используются для управления действиями компилятора над конкретной частью программы без воздействия на программу в целом. (Синтаксис прагм рассмотрен в Разделе 8.6.) Однако, доступность и предназначение отдельных прагм определяется конкретной реализацией компилятора. Информацию об использовании и действии конкретных прагм можно найти в Вашем Руководстве по компилятору.

8.2 Объявленные константы и Макросы

Директива #define обычно используется для организации связи содержательных идентификаторов с константами, ключевыми словами и часто используемыми операторами и выражениями. Представляющие константы идентификаторы называются "объявленными константами". Представляющие операторы или выражения константы называются "макросами".

После определения идентификатора его нельзя переопределить для другого значения, если не удалить первоначальное определение. Однако, можно переопределить идентификатор тем же самым определением. Следовательно, одно определение может появиться в программе несколько раз.

Директива #undef удаляет определение идентификатора. После удаления определения идентификатор можно переопределить другим значением. Директивы #define и #undef рассматриваются соответственно в Разделах 8.2.2 и 8.2.3.

Практически можно выделить два типа макросов. "Объектные" макросы не принимают аргументов, а "функциональные" определены таким образом, что принимают аргументы, и выглядят и действуют подобно вызовам функций. Макросы не генерируют действительные вызовы функций, поэтому программа будет работать быстрее, если заменить вызовы функций макросами. Однако, макросы могут создать свои проблемы, если не подойти к их определению и использованию со всей тщательностью. В определении макросов с аргументами возможно придется воспользоваться скобками для установления надлежащего порядка проведения вычислений в выражениях. Кроме того, макросы могут некорректно обработать выражения с побочными эффектами. Дополнительную информацию можно увидеть в примерах Раздела 8.2.2, "Директива #define".

Операторы предпроцессора

Есть три специфических оператора предпроцессора: один представлен знаком номера (#), другой удвоенным знаком номера (##), а третий словом defined. "Строковый" оператор (#), который предшествует имени формального параметра макро, заставляет предпроцессор заключить соответствующий действительный аргумент в строковые цитатные скобки. Оператор "вставки лексем" (##) позволяет осуществить слияние лексем, заданных в качестве действительных аргументов, в форму другой лексемы. Эти два оператора используются в контексте директивы #define и описаны в Разделах 8.2.2.1 и 8.2.2.2.

И, наконец, оператор defined упрощает написание составных выражений в некоторых директивах макро. Он используется при условной компиляции и поэтому рассмотрен в Разделе 8.4.1 "Директивы #if, #elif, #else и #endif".

Директива #define

      Синтаксис: #define идентификатор текст-замены   
                 #define идентификатор(список-параметров)   
                     текст-замены   
Директива #define заменяет все появления "идентификатор" в исходном файле на "текст-замены". Идентификатор заменяется только тогда, когда он формирует лексему. (Лексемы описаны в Главе "Элементы языка С" и "Кратком обзоре синтаксиса".) Например, идентификатор не будет заменен, если он появляется в строке или как часть более длинного идентификатора.

Если после идентификатора следует список параметров, то директива #define заменит каждое появление идентификатор(список-параметров) на версию аргумента текст-замены в которой формальные параметры заменены на действительные аргументы.

Аргумент "текст-замены" состоит из ряда лексем, таких как ключевые слова, константы или полные операторы. Один или несколько разделительных символов должны отделять текст-замены от идентификатора (или от закрывающей скобки списка параметров). Эти разделительные символы не считаются частью текста-замены, также как и любые разделительные символы, которые следуют за последней лексемой текста. Текст, занимающий более одной строки, может быть продолжен на следующей строке, если до символа перехода на новую строку поместить знак обратного деления (\).

Аргумент текст-замены может быть пустым. Выбор этой опции удаляет все появления идентификатора из исходного файла. Однако, идентификатор все еще считается определенным и дает значение 1 при его проверке директивой #if (рассматривается в Разделе 8.4.1).

Необязательный список параметров состоит из одного или нескольких имен формальных параметров, разделенных запятыми. Каждое имя списка должно быть уникальным и весь список должен быть заключен в скобки. Нельзя ставить пробел между идентификатором и открывающей скобкой. Сфера действия формального параметра распространяется до той строки, которая заканчивает текст-замены.

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

Действительные аргументы, которые следуют за появлениями идентификатора в исходном файле совпадают с соответствующими формальными параметрами списка параметров. Каждый формальный параметр в тексте замены, которому не предшествует оператор # или ##, или за которым следует оператор ##, будет заменен соответствующим действительным аргументом. Все макросы действительного аргумента будут раскрыты до замены ими формального параметра. (Операторы # и ## описаны в Разделах 8.2.2.1 и 8.2.2.2.) Список действительных аргументов должен иметь столько аргументов, сколько их в списке параметров.

Если имя определенного макро появляется в тексте замены (даже в результате раскрытия другого макро), то оно не раскрывается.

Аргументы с побочными эффектами иногда вынуждают макро выдавать неожиданные результаты. Заданный формальный параметр может появиться в тексте замены несколько раз. Если формальный параметр заменяется выражением с побочным эффектом, то выражение, с его побочным эффектом, будет вычисляться несколько раз. (См. Пример 4 в Разделе 8.2.2.2, "Оператор вставки лексем".

Строковый оператор (#)

Знак номера или строковый оператор (#) используется только с макросами, которые принимают аргументы. Если в определении макро он предшествует формальному параметру, то передаваемый при вызове макро действительный аргумент заключается в цитатные скобки и обрабатывается как строковый литерал. Затем строковый литерал заменяет каждое появление комбинации строкового оператора и формального параметра в определении макро. Предшествующие первой лексеме действительного аргумента разделительные символы и следующие за последней лексемой действительного аргумента разделительные символы игнорируются. В результирующем строковом литерале разделительные символы между лексемами действительного аргумента сокращаются до одного. Следовательно, если между двумя лексемами действительного аргумента есть комментарий, он будет сокращен до одного разделительного символа. Результирующий строковый литерал автоматически сливается с любым соседним строковым литералом от которого он разделен лишь разделительным символом. Более того, если аргумент содержит символ, который обычно требует управляющей последовательности для использования в качестве строкового литерала - например, цитатная скобка (") или знак обратного деления (\) - то перед этим символом будет автоматически вставлен необходимый для управляющей последовательности знак обратного деления. Следующий пример показывает определение макро, которое содержит строковый оператор, и функцию main, которая вызывает это макро:

 #define stringer(x) printf(#x "\n")   
   
  main()   
  {   
   stringer (I will be in quotes in the printf function call);   
   stringer ("I will be in quotes when printed to the screen");   
   stringer (This: \" prints an escaped double quote mark);   
  }   

В результате предпроцессорной обработки получим следующий код:

 printf("I will be in quotes in the printf function call" "\n");   
 printf("\"I will be in quotes when printed to the screen\"" "\n");   
 printf(This: \\\" prints an escaped double quote mark");   
При работе программы на экран выведется следующий текст:

      I will be in quotes in the printf function call   
      "I will be in quotes when printed to the screen"   
      This: \" prints an escaped double quote mark   

Оператор вставки лексем (##)

Удвоенный знак номера или оператор "вставки лексем" (##) используется и в объектный и в функциональных макро. Он позволяет объединять отдельные лексемы в единую лексему и следовательно не может быть первой или последней лексемой в определении макро.

Если до или после формального параметра в определении макро стоит оператор вставки лексем, то формальный параметр немедленно заменяется на нераскрытый действительный аргумент. Раскрытие макро не производится над аргументом до его замены. Затем все появления оператора с тексте замены удаляются и предшествующие и последующие им лексемы объединяются. Результирующая лексема должна быть корректной. Если это так, то лексема просматривается на возможную замену, если она содержит имя макро. Пример 7 показывает, как лексемы могут быть вставлены вместе с использованием оператора вставки лексем.

Пример 1

В данном примере определяется идентификатор WIDTH как целая константа 80, в терминах WIDTH определяется LENGTH и целая константа 10. Каждое появление LENGTH заменяется на (WIDTH + 10). В свою очередь, каждое появление WIDTH + 10 заменяется выражением (80+10).

           #define WIDTH  80   
           #define LENGTH (WIDTH + 10)   
Замыкающие WIDTH + 10 скобки очень важны, т.к. они управляют интерпретацией операторов, подобных следующему:

           var = LENGTH * 20;   
После стадии предпроцессорной обработки этот оператор будет выглядеть так:

           var = (80 + 10) * 20;   
что даст в результате 1800. Без скобок результат будет 280:

           var = 80 + 10 * 20;   

Пример 2

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

      #define FILEMASSAGE "Attempt to create file \   
      failed because of insufficient space"   

Пример 3

В данном примере определяются три идентификатора: REG1, REG2 и REG3. REG1 и REG2 определяются как ключевое слово register. Определение REG3 пусто, поэтому из исходного файла будет удалены все появления REG3. Эти директивы можно использовать для того, чтобы убедиться в том, что наиболее важные переменные программы (объявленные с REG1 и REG2) заданы с классом хранения register. (Расширенную версию данного примера можно найти при рассмотрении директивы #if в Разделе 8.4.1.)

           #define REG1   register   
           #define REG2   register   
           #define REG3   

Пример 4

В данном примере определяется макро с именем MAX. После этого определения все появления идентификатора MAX в исходном файле будут заменены на выражение

           ((x) > (y)) ? (x) : (y)   
где действительные значения заменят параметры x и y. Например, появление

           MAX(1,2)   
будет заменено на

           ((1) > (2)) ? (1) : (2)   
и появление

           MAX(i,s[i])   
будет заменено на

           ((i) > (s[i])) ? (i) : (s[i])   

           #define MAX(x,y)    ((x) > (y)) ? (x) : (y)   

Макро легче читается, чем соответствующее выражение, поэтому легче понять исходный текст программы.

Обратите внимание на то, что аргументы с побочными эффектами могут дать неожиданные результаты при выполнении макро. Например, появление MAX(i, s[i++]) будет заменено на ((i)>(s[i++]))?(i): (s[i++]). Выражение (s[i++]) может быть вычислено дважды, поэтому после вычисления тернарного оператора i может быть увеличено единожды или дважды, в зависимости от результатов сравнения.

Пример 5

В данном примере определяется макро MULT. После определения этого макро появление выражений подобных MULT(3,5) будет заменены на (3)*(5). Скобки вокруг параметров важны, т.к. управление интерпретацией сложных выражений формирует аргументы макро. Например, появление MULT(3+4,5+6) будет заменено на (3+4)*(5+6), что даст в результате 77. Без скобок получаем 3+4*5+6, или 29, т.к. оператор умножения (*) имеет приоритет выше, чем оператор сложения (+).

           #define MULT(a,b)   ((a) * (b))   

Пример 6

В данном примере определяются два макро. Объектное макро раскрывается в строковый литерал Hello,Word!, а другое функциональное макро вызывает show, которое берет один аргумент. Однако, определение второго макро включает строковый оператор (#), который непосредственно предшествует формальному параметру x. При передаче аргумента в макро show формальный параметр будет заменен действительным аргументом, заключенным в двойные цитатные скобки.

           #define GREETING Hello, World!   
           #define show(x) printf(#x)   
   
           main()   
           {   
                show(x+z);   
                printf("\n");   
                show(n /* some comment */ + p);   
                printf("\n");   
                show(GREETING); /* GREETING не раскрыто; */   
                printf("\n");   /* вместо этого отработал */   
                show ('\x');    /* строковый оператор */   
                }   
По мере обработки предпроцессором исходного файла ссылки на show будут раскрыты так:

      show (x+z); даст printf("x+z");   
      show (n/*ccomment*/+p); даст printf("n+p");   
      show (GREETING); даст printf("GREETING");   
      show ('\x'); даст printf("'\\x'");   
При работе программы на экран будет выведено:

      x+z   
      n+p   
      GREETING   
      '\x'   

Пример 7

Данный пример показывает использование строкового оператора и оператора вставки лексем для организации вывода программы.

      #define paster(n) printf("token" #n " = %d", token##n)   
Если объявлен token9 и макро вызывается с числовым аргументом, подобно:

           paster(9);   
то макро преобразуется в вид:

           printf("token" "9" " = %d", token9);   
который станет

           printf("token9 = %d", token9);   

Директива #undef

      Синтаксис:     #undef идентификатор   
Директива #undef удаляет текущее определение идентификатора. Поэтому все встречающиеся появления идентификатора будут игнорироваться предпроцессором. Для удаления определения макро с использованием #undef, нужно задать только идентификатор макро, не задавая список параметров.

Можно применить директиву #undef к идентификатору, у которого нет определения. Тем самым пользователь получает дополнительную гарантию того, что данный идентификатор не определен.

Директива #undef обычно используется в паре с директивой #define для задания области исходной программы, в которой идентификатор имеет специальное значение. Например, некоторая функция исходной программы может иметь объявленные константы, которые задают значения среды работы, которые не влияют на остальную часть программы. Директива #undef также работает с директивой #if (см. Раздел 8.4.1) для управления условной компиляцией исходной программы.

Пример

В данном примере директива #undef удаляет определения объявленных констант и макро. Обратите внимание на то, что задается только идентификатор макро.

           #define WIDTH  80   
           #define ADD(X,Y)    (X) + (Y)   
           .   
           .   
           .   
           #undef WIDTH   
           #undef ADD   

8.3 Включаемые файлы

      Синтаксис:     #include "спецификация-пути"   
                     #include <спецификация-пути>   
v Директива #include добавляет содержимое заданного файла в другой файл. Можно организовать определения констант и макро в отдельном файле, а затем вставить его директивой #include в любой другой файл. Вставка файлов также очень удобна для объединения объявлений внешних переменных и сложных типов данных. Нужно определить и задать имена этих типов только один раз в созданный для этих целей файл.

Директива #include информирует предпроцессор о том, что содержание файла с заданным именем следует обрабатывать так, как будто оно присутствует в исходной программе в месте расположения этой директивы. Новый текст также может содержать директивы предпроцессора. Предпроцессор выполняет директивы в новом тексте, а затем продолжает обработку текста исходного файла.

"Спецификация пути" это имя файла, которому может предшествовать директория. Это должно быть имя существующего файла. Синтаксис спецификации файла зависит от операционной системы, в которой компилируется программа.

При поиске файлов предпроцессор использует концепцию "стандартной" директории. Расположение стандартных директорий для файлов зависит от реализации и операционной системы. Определение стандартной директории можно найти в Вашем Руководстве по компилятору.

Предпроцессор останавливает поиск сразу же после обнаружения файла с заданным именем. Если задать полную спецификацию файла, заключенную в двойные цитатные скобки (" "), то предпроцессор использует ее для поиска и игнорирует стандартную директорию.

Если заключенная в двойные цитатные скобки спецификация файла является неполной, то предпроцессор сначала ищет директорию "родительского" файла. Родительский файл это файл, содержащий директиву #include. Например, если файл fil2 вставляется в файл fil1, то fil1 будет родительским файлом.

Вставка файлов может быть вложенной. Т.е. директива #include может появляться в файле, который сам вставляется директивой #include. Например, в приведенном выше примере файл fil2 может вызывать файл fil3. В этом случае fil1 все еще будет родительским для fil2, но "дедушкой" для fil3.

При вложенной вставке файлов поиск директории начинается с родительского файла, затем проходит по дедушкиным файлам. Следовательно, поиск начинается в директории, которая содержит обрабатываемый исходный файл. Если файл не найден, то поиск продолжается в директориях, заданных в командной строке компилятора. И, наконец, производится поиск в стандартной директории.

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

Допускается вложение вставки файлов до 10 уровней. При обработке вложенных #include предпроцессор всегда будет осуществлять вставку в первоначальный исходный файл.

Пример 1

В данном примере содержимое файла с именем stdio.h будет вставлено в исходную программу. Угловые скобки заставляют предпроцессор искать stdio.h в стандартной директории, после поиска в директориях, указанных в командной строке.

                #include <stdio.h>   

Пример 2

Данный пример добавит в исходный файл содержимое файла defs.h. Двойные цитатные скобки означают, что предпроцессор начнет поиск в родительском каталоге.

                #include "defs.h"   

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

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

Директивы #if, #elif, #else и #endif

      Синтаксис:     #if граничное-постоянное-выражение   
                          [блок-текста]   
                     [#elif граничное-постоянное-выражение   
                          блок-текста]   
                     [#elif граничное-постоянное-выражение   
                          блок-текста]   
                     .   
                     .   
                     .   
                     [#else   
                          блок-текста]   
                     #endif   
Директива #if вместе с директивами #elif, #else и #endif управляют компиляцией части исходного файла. Каждая директива #if в исходном файле должна иметь соответствующую закрывающую директиву #endif. Между директивами #if и #endif может появиться любое число директив #elif, но допускается наличие только одной директивы #else. Если имеется директива #else, то она должна быть последней директивой перед #endif.

Предпроцессор выбирает одно из заданных появлений блока текста для дальнейшей обработки. Этот блок может быть любой последовательностью текста. Он может занимать несколько строк. Обычно блок текста это текст программы, который имеет значение для компилятора или предпроцессора.

Предпроцессор обрабатывает выбранный блок текста и передает его компилятору. Если блок текста содержит директивы предпроцессора, то предпроцессор выполнит эти директивы.

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

Предпроцессор выбирает отдельный блок текста вычисляя выражение граничной константы, которое следует за каждой директивой #if или #elif, пока результатом выражения граничной константы не будет "истина" (не ноль). Выбирается весь текст (включая начинающиеся с # другие директивы предпроцессора) до соответствующего #elif, #else или #endif.

Если значением всех выражений граничных констант будет "ложь", или нет директивы #elif, то предпроцессор выберет блок текста после предложения #else. Если предложения #else нет, то блок вообще не выбирается.

Каждое выражение граничной константы соответствует правилам Раздела 5.2.10. Эти выражения не могут содержать выражений sizeof, приведения типа или перечислимых констант. Однако, они могут содержать оператор предпроцессора defined в специальном постоянном выражении, имеющем следующий синтаксис:

                defined(идентификатор)   
Это постоянное выражение будет иметь значение "истина" (не ноль), если заданный идентификатор определен, в противном случае - "ложь" (0). Идентификатор, определенный как пустой текст, считается определенным.

Директивы #if, #elif, #else и #endif могут быть вложены в другие директивы #if. Каждая вложенная директива #else, #elif или #endif принадлежит к ближайшей к ней директиве #if.

Пример 1

В данном примере директивы #if и #endif управляют компиляцией одного из трех вызовов функции. Вызов функции credit компилируется, если определен идентификатор CREDIT. Если определен идентификатор DEBIT, то компилируется вызов функции debit. Если не определен ни один из идентификаторов, то компилируется вызов printerror. Обратите внимание на то, что CREDIT и credit это разные идентификаторы в языке С.

           #if defined(CREDIT)   
                credit();   
           #elif defined(DEBIT)   
                debit();   
           #else   
                printerror();   
           #end   

Пример 2

Примеры 2 и 3 предполагают наличие ранее определенной константы с именем DLEVEL.

Пример 2 показывает два вложенных набора директив #if, #else и #endif. Первый набор директив будет обработан только если значение DLEVEL>5 "истина". В противном случае будет обработан второй набор директив.

           #if DLEVEL>5   
                #define SIGNAL 1   
                #if STACKUSE == 1   
                     #define STACK 200   
                #else   
                     #define STACK 100   
                #endif   
           #else   
                #define SIGNAL 0   
                #if STACKUSE == 1   
                     #define STACK 100   
                #else   
                     #define STACK 50   
                #endif   
           #endif   
v

Пример 3

В примере 3 директивы #elif и #else применятся для того, чтобы сделать выбор одного из четырех вариантов на основании значения DLEVEL. Объявленная константа STACK устанавливается в 0, 100 или 200 в зависимости от определения DLEVEL. Если DLEVEL больше 5, то компилируется display(debugptr); и STACK не определяется.

           #if DLEVEL == 0   
                #define STACK 0   
           #elif DLEVEL == 1   
                #define STACK 100   
           #elif DLEVEL > 5   
                display(debugptr);   
           #else   
                #define STACK 200   
           #endif   

Пример 4

В данном примере директивы предпроцессора используются для управления значением объявления register в мобильном исходном файле. Компилятор выделяет регистровую память переменным в той последовательности, в которой объявления register появляются в исходном файле. Если в программе присутствуют больше объявлений register, чем допускает компьютер, то компилятор дает приоритет более ранним объявлениям над более поздними. Программа может стать менее эффективной, если наиболее часто используемые переменные объявлены слишком поздно.

           #define REG1 register   
           #define REG2 register   
   
           #if defined(M_86)   
                #define REG3   
                #define REG4   
                #define REG5   
   
           #else   
                #define REG3 register   
                #if defined(M_68000)   
                     #define REG4 register   
                     #define REG5 register   
                #else   
                     #define REG4 register   
                     #define REG5   
                #endif   
           #endif   
Приведенные в Примере 4 определения можно использовать для задания высшего приоритета наиболее важным объявлениям регистров. REG1 и REG2 определены с ключевым словом register и для этих двух наиболее важных переменных программы выделяется регистровая память. Например, в следующем фрагменте b и c имеют приоритет выше, чем a и d:

                func(a)   
 
                REG3 int a;   
   
                {   
                     REG1 int b;   
                     REG2 int c;   
                     REG4 int d;   
                     .   
                     .   
                     .   
                }   
Если определен M_86, то предпроцессор удалит идентификатор REG3 из файла, заменив его пустым текстом. а не будет выделена регистровая память за счет b и с. Если задан M_68000, то всем четырем переменным выделяется регистровая память. Если не задан ни М_86 ни М_68000, то регистровая память выделяется а, b и с.

Директивы #ifdef и #ifndef

      Синтаксис:     #ifdef идентификатор   
                     #ifndef идентификатор   
Директивы #ifdef и #ifndef выполняют те же функции, что и директива #if с defined(идентификатор). Директивы #ifdef и #ifndef можно использовать везде, где допустимо использование #if. Эти директивы реализованы только для обеспечения совместимости с предыдущими версиями языка. Предпочтительно использовать постоянное выражение defined(идентификатор) c с директивой #if.

Когда предпроцессор обнаруживает директиву #ifdef, он проверяет, определен ли идентификатор. Если это так, то значение условия "истина" (не ноль), и "ложь" (0) в противном случае.

Директива #ifndef проверяет условие, противоположное условию #ifdef. Если идентификатор не определен (или его определение удалено с помощью #undef), то условие "истина", и "ложь" (0) в противном случае.

8.5 Управление Line

      Синтаксис:     #line константа["имя-файла"]   
Директива #line дает команду компилятору сменить хранимые им номер строки и имя файла на данные. Компилятор использует номер строки и имя файла для указания ошибок, которые он обнаруживает при компиляции. Номер строки обычно соответствует текущей строке, а имя файла - текущему файлу. После обработки каждой строки номер строки увеличивается.

Если сменить номер строки и имя файла, то компилятор игнорирует предыдущие значения и продолжит обработку с новыми значениями. Директива #line обычно используется генераторами программ для появления сообщений об ошибках в исходном тексте, а не в генерируемой программе.

Константа в директиве #line может быть любой целой константой. Имя файла может быть любой комбинацией символов, заключенной в двойные цитатные скобки. Если имя файла не задано, то предыдущее имя файла остается без изменения.

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

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

Пример 1

В данном примере хранимый номер строки меняется на 151, а имя файла меняется на copy.c .

                #line 151 "copy.c"   

Пример 2

В данном примере макро ASSERT использует ранее определенные идентификаторы __LINE__ и __FILE__ для печати сообщения об ошибке в исходном файле, если значение условия "ложь".

           #define ASSERT(cond)     if(!cond)\   
           {printf("assertion error line %d, file(%s)\n",\   
           __LINE__, __FILE__ );} else   

8.6 Прагмы

      Синтаксис:     #pragma последовательность-символов   
#pragma это инструкция компилятору, которая определяется реализацией. Последовательность символов задает конкретную инструкцию компилятору и аргументы, если они есть. Знак номера (#) должен быть первым неразделительным символом на строке, содержащей прагму. Между знаком номера и словом pragma могут стоять разделительные символы.

Информация об имеющихся в реализации Вашего компилятора прагмах содержится в Вашем Руководстве по компилятору.


Вперед Назад Содержание


Спонсоры:
Слёрм
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2020 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру