на тему рефераты
 
Главная | Карта сайта
на тему рефераты
РАЗДЕЛЫ

на тему рефераты
ПАРТНЕРЫ

на тему рефераты
АЛФАВИТ
... А Б В Г Д Е Ж З И К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Э Ю Я

на тему рефераты
ПОИСК
Введите фамилию автора:


Реферат: Препроцессор языка C.


     Возможно объединение макро вызова, исходящего частично от тела макроса

и частично - от аргументов. Например,

     #define double(x) (2*(x))

     #define call_with_1(x) x(1)

здесь строка 'call_with_1 (double)' будет заменена на '(2*(1))'.

     Макроопределения не обязательно должны иметь закрывающиеся скобки. Путем

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

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

Например,

     #define strange(file) fprintf (file, "%s %d",

     ...

     strange(stderr) p, 35)

     В результате обработки этого странного примера получится строка

'fprintf (stderr, "%s %d", p, 35)'.

     4.8.2. Нестандартная группировка арифметических выражений

     Во большинстве примеров макроопределений, рассмотренных выше, каждое

имя макроаргумента заключено в скобки. В дополнение к этому, другая пара

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

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

     Допустим, существует следующее макроопределение:

     #define ceil_div(x, y) (x + y - 1) / y

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

используется следующим образом:

     a = ceil_div (b & c, sizeof (int));

     В результате эта строка заменяется на

     a = (b & c + sizeof (int) - 1) / sizeof (int);

которая не выполняет требуемой задачи. Правила приоритета операторов С

позволяют написать следующую сроку:

     a = (b & (c + sizeof (int) - 1)) / sizeof (int);

но требуется

     a = ((b & c) + sizeof (int) - 1)) / sizeof (int);

Если определить макрос следующим образом:

     #define ceil_div(x, y) ((x) + (y) - 1) / (y)

то будет получен желаемый результат.

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

Рассмотрим выражение 'sizeof ceil_div(1, 2)'. Здесь используется выражение

С, вычисляющее размер типа данных 'ceil_div(1, 2)', но в действительности

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

на следующую:

     sizeof ((1) + (2) - 1) / (2)

     Здесь определяется размер типа целого значения и делится пополам.

Правила приоритета помещают операцию деления вне поля действия операции

'sizeof', в то время как должен определяться размер всего выражения.

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

проблем. Далее дан правильный пример определения макроса 'ceil_div'.

     #define ceil_div(x, y) (((x) + (y) - 1) / (y))

     4.8.3.  Использование точки с запятой

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

конструкциях. Рассмотрим следующий макрос, который использует указатель

(аргумент 'p' указывает его местоположение):

     #define SKIP_SPACES (p, limit)  \

     { register char *lim = (limit); \

       while (p != lim) {            \

         if (*p++ != ' ') {          \

           p--; break; }}}

     Здесь последовательность backslash-newline используется для разбиения

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

строке.

     Вызов этого макроса может выглядеть так: 'SKIP_SPACES (p, lim)'. Грубо

говоря, при его вызове он заменяется на составную конструкцию, которая

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

запятой для ее завершения. Но вызов этого макроса выглядит как вызов функции.

Поэтому удобнее будет вызывать этот макрос следующим образом:

'SKIP_SPACES (p, lim);'

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

выражением 'else', так как точка с запятой является пустым выражением.

Рассмотрим такой пример:

     if (*p != 0)

       SKIP_SPACES (p, lim);

     else ...

     Использование двух выражений (составной конструкции и пустого выражения)

между условием 'if' и конструкцией 'else' создает неправильный С код.

     Определение макроса 'SKIP_SPACES' может быть изменено для устранения

этого недостатка с использованием конструкции 'do ... while'.

     #define SKIP_SPACES (p, limit)     \

     do { register char *lim = (limit); \

          while (p != lim) {            \

            if (*p++ != ' ') {          \

              p--; break; }}}           \

     while (0)

     Теперь макрос 'SKIP_SPACES (p, lim);' заменяется на

     do {...} while (0);

что является одним выражением.

     4.8.4.  Удвоение побочных эффектов

     Во многих С программах определяется макрос 'min' для вычисления

минимума:

     #define min(X, Y)  ((X) < (Y) ? (X) : (Y))

     При вызове этого макроса вместе с аргументом, содержащим побочный

эффект, следующим образом:

     next = min (x + y, foo (z));

он заменяется на строку

     next = ((x + y) < (foo (z)) ? (x + y) : (foo (z)));

где значение 'x + y' подставляется вместо 'X', а 'foo (z)' - вместо 'Y'.

     Функция 'foo' используется в этой конструкции только один раз, в то

время как выражение 'foo (z)' используется дважды в макроподстановке. В

результате функция 'foo' может быть вызвана дважды при выполнении выражения.

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

аргументов требуется много времени, результат может быть неожиданным. В

данном случае макрос 'min' является ненадежным.

     Наилучшим решением этой проблемы является определение макроса 'min'

таким образом, что значение 'foo (z)' будет вычисляться только один раз. В

языке С нет стандартных средств для выполнения подобных задач, но с

использованием расширений GNU C это может быть выполнено следующим образом:

     #define min(X, Y)                     \

     ({ typeof (X) __x = (X), __y = (Y);   \

        (__x < __y) ? __x : __y; })

     Если не использовать расширения GNU C, то единственным решением будет

осторожное применение макроса 'min'. Например, для вычисления значения

'foo (z)', можно сохранить его в переменной, а затем использовать ее значение

при вызова макроса:

     #define min(X, Y)  ((X) < (Y) ? (X) : (Y))

     ...

     {

       int tem = foo (z);

       next = min (x + y, tem);

     }

(здесь предполагается, что функция 'foo' возвращает значение типа 'int').

     4.8.5. Рекурсивные макросы

     "Рекурсивные" макросы - это макросы, в определении которых используется

имя самого макроса. Стандарт ANSI C не рассатривает рекурсивный вызов

макроса как вызов. Он поступает на вывод препроцессора без изменений.

     Рассмотрим пример:

     #define foo (4 + foo)

где 'foo' также является переменной в программе.

     Следуя обычным правилам, каждая ссылка на 'foo' заменяется на значение

'(4 + foo)', затем это значение просматривается еще раз и заменяется на

'(4 + (4 + foo))' и так далее, пока это не приведет к ошибке (memory full)

препроцессора.

     Однако, правило об использовании рекурсивных макросов завершит этот

процесс после получения результата '(4 + foo)'. Поэтому этот макрос может

использоваться для прибавления 4 к значению переменной 'foo'.

     В большинстве случаев не следует опираться на эту возможность. При

чтении исходных текстов может возникнуть путаница между тем, какое значение

является переменной, а какое - вызовом макроса.

     Также используется специальное правило для "косвенной" рекурсии. Здесь

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

является макросом и заменяется на значение 'x'. В результате ссылка на

макрос 'x' является косвенной и происходит от подстановки макроса 'x', таким

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

обработки

     #define x (4 + y)

     #define y (2 * x)

'x' заменяется на '(4 + (2 * x))'.

     Но предположим, что 'y' используется где-либо еще и не в определении

макроса 'x'. Поэтому использование значения 'x' в подстановке макроса 'y'

не является рекурсией. Таким образом, производится подстановка. Однако,

подстановка 'x' содержит ссылку на 'y', а это является косвенной рекурсией.

В результате 'y' заменяется на '(2 * (4 + y))'.

     Неизвестно где такие возможности могут быть использованы, но это

определено стандартом ANSI C.

     4.8.6.  Отдельная подстановка макро аргументов

     Ранее было объяснено, что макроподстановка, включая подставленные

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

вызовов.

     Что же происходит на самом деле, является довольно тонким моментом.

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

Затем полученные значения подставляются в тело макроса и полученная макро

подстановка проверяется еще раз на наличие новых макросов.

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

     В большинстве случаев это не дает никакого эффекта. Если аргумент

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

Полученное значение не содержит макро вызовов и при втором проходе оно не

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

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

макроподстановка.

     Рекурсивный макрос один раз подставляется при первом проходе, а второй

раз - при втором. Не подставляемые рекурсивные элементы при выполнении

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

втором.

     Первый проход не выполняется, если аргумент образован путем

стрингификации или объединения. Поэтому

     #define str(s) #s

     #define foo 4

     str (foo)

заменяется на '"foo"'.

     При стрингификации и объединении аргумент используется в таком виде, в

каком он был указан без последующего просмотра его значения. Этот же аргумент

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

стрингификации или объединения.

     #define str(s) #s lose(s)

     #define foo 4

     str (foo)

заменяется на '"foo" lose(4)'.

     Возникает вопрос: для чего используется два прохода для просмотра

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

препроцессора. В действительности, здесь есть некоторая разница и она

может быть видна в трех отдельных случаях:

     При однородных вызовах макросов.

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

используют стрингификацию или объединение.

     При использовании макросов, содержащих открытые запятые.

     Макро вызовы называются "однородными", если аргумент этого макроса

содержит вызов этого же макроса. Например, 'f' это макрос, принимающий

один аргумент, а 'f (f (1))' является однородной парой вызовов макроса 'f'.

Требуемая подстановка производится путем подстановки значения 'f (1)' и

его замены на определение 'f'. Дополнительный проход приводит к желаемому

результату. Без его выполнения значение 'f (1)' будет заменено как

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

рекурсивным элементом. Таким образом, применение второго прохода

предотвращает нежелательный побочный эффект правила о рекурсивных макросах.

     Но применение второго прохода приводит к некоторым осложнениям в

отдельных случаях при вызовах однородных макросов. Рассмотрим пример:

     #define foo  a,b

     #define bar(x) lose(x)

     #define lose(x) (1 + (x))

     bar(foo)

     Требуется преобразовать значение 'bar(foo)' в '(1 + (foo))', которое

затем должно быть преобразовано в '(1 + (a,b))'. Но вместо этого,

'bar (foo)' заменяется на 'lose(a,b)' что в результате приводит к ошибке,

так как 'lose' принимает только один аргумент. В данном случае эта проблема

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

арифметических операций:

     #define foo (a,b)

     #define bar(x) lose((x))

     Проблема становится сложнее, если аргументы макроса не являются

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

скобок неприменимо, так как это может привести к неправильному С коду:

     #define foo { int a, b; ... }

     В GNU C запятые можно закрыть с помощью  '({...})', что преобразует

составную конструкцию в выражение:

     #define foo ({ int a, b; ... })

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

     #define foo { int a; int b; ... }

     Существует также еще один случай, когда применяется второй проход. Его

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

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

'xstr' к рассмотренному выше примеру:

     #define xstr(s) str(s)

     #define str(s) #s

     #define foo 4

     xstr (foo)

     Здесь значение 'xstr' заменяется на '"4"', а не на '"foo"'. Причиной

этому служит то, что аргумент макроса 'xstr' заменяется при первом проходе

(так как он не использует стрингификацию или объединение аргумента). В

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

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

стрингификация.

     4.8.7. Зависимые макросы

     "Зависимым" макросом называется макрос, тело которого содержит ссылку

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

     #define BUFSIZE 1020

     #define TABLESIZE BUFSIZE

     Это не является определением макроса 'TABLESIZE' со значением '1020'.

Директива '#define' для макроса 'TABLESIZE' использует в точности тело

указанного макроса, в данном случае это 'BUFSIZE'.

     Подстановка значения 'TABLESIZE' производится только при использовании

этого макроса.

     При изменении значения 'BUFSIZE' в каком-либо месте программы ее

выполнение меняется. Макрос 'TABLESIZE', определенный как было описано выше,

всегда заменяется с использованием значения макроса 'BUFSIZE':

     #define BUFSIZE 1020

     #define TABLESIZE BUFSIZE

     #undef BUFSIZE

     #define BUFSIZE 37

     Теперь значение 'TABLESIZE' заменяется (в две стадии) на '37'.

     4.9. Символы newline в макроаргументах

     При обычной обработке макросов все символы newline в макроаргументах

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

подставляются более одного раза или вообще не подставляются, то символы

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

конструкций, то в результате порядок строк этих конструкций будет нарушен.

Это может привести к неправильным значениям номеров строк в сообщениях об

ошибках или при работе с отладчиком.

     При работе GNU C препроцессора в режиме ANSI C, им контролируется

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

подставляются все символы newline, а при последующем использовании эти

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

ошибочная нумерация строк если аргументы используются не в надлежащем

порядке или вообще не используются.

     Рассмотрим пример:

     #define ignore_second_arg(a,b,c) a; c

     ignore_second_arg (foo (),

                        ignored (),

                        syntax error);

     Синтаксическая ошибка со ссылкой на четвертую строку получается в

результате обработки строки 'syntax error', хотя ошибочное выражение

находится в пятой строке.

     5.  Условия

     В работе препроцессора "условием" называется директива, при выполнении

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

некоторых условий. В С препроцессоре в условии могут использоваться как

арифметические выражения, так и имена определенных макросов.

     Условие в С препроцессоре в некоторых аспектах имеет сходство с

конструкцией 'if' языка С, но важно понимать их отличия. Условие в

конструкции 'if' проверяется при выполнении программы. Ее целью служит

изменение хода программы в зависимости от обрабатываемых данных. Условие в

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

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

установленных при ее компиляции.

     5.1. Для чего используются условия

     Существует три основных причины для применения условий.

     Для выполнения программы на различных платформах может потребоваться

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

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

системе. В подобных ситуациях недостаточно уклонения от выполнения

ненужных процедур. Если программа их содержит, то часто случается, что

невозможно скомпоновать и запустить программу. При использовании условной

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

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

программы. Случается, что различия в программах заключаются в том, что

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

или выдает значения этих данных для отладки, в то время как другая не

делает этого.

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

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

одним из видов комментариев для ссылки на него в будующем.

     В большинстве простых программ, предназначенных для выполнения только

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

     5.2. Синтаксис условий

     Условие в С препроцессоре начинается с директивы условия: '#if',

'#ifdef' или '#ifndef'. Далее рассматривается только директива '#if'.

     5.2.1. Директива '#if'

     Простейшая форма использования директивы '#if' рассмотрена ниже.

     #if EXPRESSION

     CONTROLLED TEXT

     #endif /* EXPRESSION */

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

но помогает при написании и чтении программы. Такие комментарии всегда

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

действительности, текст в строке после директивы '#endif' игнорируется

GNU C препроцессором, но стандарт ANSI C требует применения комментариев.

     Выражение EXPRESSION является С выражением типа integer, что

представляет собой сильное ограничение. Оно может содержать:

     Целые константы, которые рассматриваются как тип 'long' или 'unsigned

long'.

     Символьные константы, которые интерпретируются в соответствии с набором

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

установлен препроцессор. Для таких констант GNU С препроцессор использует

тип данных 'char'. Поэтому являются ли коды некоторых символов отрицательными

значениями, определяется С компилятором, который использовался для

компиляции препроцессора. Если тип 'char' является знаковым, то символы,

значения которых достаточно велики для установки знакового бита,

рассматриваются как отрицательные значения. В противном случае тип 'char'

является всегда положительным значением.

     Арифметические операции сложения, вычитания, умножения, деления,

операции с битами, сдвиги, сравнения, а также логические операции ('&&' и

'||').

     Идентификаторы, не являющиеся макросами и рассматриваемые как нулевое

значение.

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

макроподстановка.

     Слдует заметить, что не допускается использовать операторы 'sizeof' и

значения типа 'enum'. Все значения типа 'enum', также как и все

идентификаторы, не являющиеся макро вызовами, рассматриваются как нулевое

значение.

     Текст, находящийся внутри условной конструкции, может включать

директивы препроцессора, которые обрабатываются при выполнении требуемых

условий. Текст может также содержать и другие условные конструкции. Однако

директивы '#if' и '#endif' должны образовывать единую конструкцию.

     5.2.2. Директива '#else'

     Директива '#else' может использоваться в условной конструкции для

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

ложно. Вот как это выглядит:

     #if EXPRESSION

     TEXT-IF-TRUE

     #else /* Not EXPRESSION */

     TEXT-IF-FALSE

     #endif /* Not EXPRESSION */

     Если значение EXPRESSION является ненулевым и используется код

TEXT-IF-TRUE, то директива '#else' рассматривается как ложное условие и

код TEXT-IF-FALSE игнорируется. И наоборот, если условие '#if' - ложно,

то включается код TEXT-IF-FALSE.

     5.2.3. Директива '#elif'

     Обычное применение однородных условий связано с проверкой более чем

двух возможных вариантов. Например:

     #if X == 1

     ...

     #else /* X != 1 */

     #if X == 2

     ...

     #else /* X != 2 */

     ...

     #endif /* X != 2 */

     #endif /* X != 1 */

     Дополнительная директива '#elif' позволяет это сократить как рассмотрено

ниже.

     #if X == 1

     ...

     #elif X == 2

     ...

     #else /* X != 2 and X != 1*/

     ...

     #endif /* X != 2 and X != 1*/

     Директива '#elif' означает "иначе если" ("else if"). Также как и

'#else', она помещается в середину конструкции '#if'-'#endif' и подразделяет

ее. Ей не требуется наличия собственной директивы '#endif'. Также как и

'#if', директива '#elif' включает в себя тестируемое выражение.

     Текст, следующий за директивой '#elif' включается только в том случае,

когда значение изходящей директивы '#if' - ложно, а условие '#elif' - верно.

В одной конструкции '#if'-'#endif' может использоваться более чем одна

директива '#elif'. Текст после директивы '#elif' включается только в том

случае, когда условие '#elif' - верно и находится после условия '#if' или

предшествующего '#elif', значения которых - ложь. '#else' является

эквивалентом директивы '#elif 1', а '#else' может следовать после

любого количества директив '#elif', в то время как '#elif' не может

следовать за '#else'.

     5.3.  Сохранение удаленного кода для дальнейших ссылок

     Если часть программы была перемещена или удалена, но есть необходимость

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

нему, то простейший способ реализации этого заключается в использовании

конструкции '#if 0'-'#endif', внутри которой находится этот код. Это

рациональнее применения обычных комментариев, так как это не всегда

помогает, если этот код также содержит комментарии.

     Такая конструкция в любом случае будет безошибочной, даже если

заключенный в нее текст также содержит условия (полные конструкции '#if'-

'#endif').

     Однако не следует применять такую конструкцию, если комментируемый

Страницы: 1, 2, 3, 4


на тему рефераты
НОВОСТИ на тему рефераты
на тему рефераты
ВХОД на тему рефераты
Логин:
Пароль:
регистрация
забыли пароль?

на тему рефераты    
на тему рефераты
ТЕГИ на тему рефераты

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


Copyright © 2012 г.
При использовании материалов - ссылка на сайт обязательна.