46010 (Препроцессор языка C.), страница 4

2016-07-31СтудИзба

Описание файла

Документ из архива "Препроцессор языка C.", который расположен в категории "". Всё это находится в предмете "информатика" из , которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "рефераты, доклады и презентации", в предмете "информатика, программирование" в общих файлах.

Онлайн просмотр документа "46010"

Текст 4 страницы из документа "46010"

или с каким-либо текстом. Полученное значение может быть именем функции,

переменной или типа, а также ключевым словом С. Оно даже может быть именем

другого макроса.

При определении макроса, проверяется наличие операторов '##' в его

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

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

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

с обоих сторон оператора '##'.

Рассмотрим С программу, интерпретирующую указываемые команды. Для этого

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

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

struct command

{

char *name;

void (*function) ();

};

struct command commands[] =

{

{ "quit", quit_command},

{ "help", help_command},

...

};

Более удобным будет не указывать имя каждой команды дважды: один раз

в строковой константе, второй - в имени функции. Макрос, принимающий в

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

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

объединения аргумента со строкой '_command'. Ниже показано как это сделать:

#define COMMAND(NAME) { #NAME, NAME ## _command }

struct command commands[] =

{

COMMAND (quit),

COMMAND (help),

...

};

Обычным объединением является объединение двух имен (или имени и какого

либо числового значения) в одно. Также возможно объединение двух числовых

значений (или числового значения и имени) в одно. Операторы, состоящие из

нескольких символов (такие как '+='), также могут быть получены с помощью

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

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

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

символа 'x', а с другой - '+' является бессмысленным с точки зрения

формирования лексических конструкций С. В стандарте ANSI указано, что

подобный тип объединения не определен, хотя препроцессор GNU C их определяет.

В данном случае он помещает вместе символы 'x' и '+' вместе без каких либо

побочных эффектов.

Следует заметить, что препроцессор С преобразует все комментарии в

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

путем объединения '/' и '*' так как последовательность символов '/*' не

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

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

сначала комментарии заменяются на пробелы, а при объединении эти пробелы

игнорируются.

4.6. Удаление макросов

"Удалить" макрос означает отменить его определение. Это производится с

помощью директивы '#undef', за которой следует имя макроса.

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

исходного файла и вступает в силу с этого места. Например,

#define FOO 4

x = FOO;

#undef FOO

x = FOO;

заменяется на

x = 4;

x = FOO;

В этом примере значение 'FOO' должно быть лучше переменной или

функцией, чем макросом, для получения после подстановки правильного С кода.

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

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

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

4.7. Переопределение макросов

"Переопределение" макроса означает определение (с помощью директивы

'#include') имени, которое уже было определено как макрос.

Переопределение явялется простым, если новое определение явно

идентично старому. Иногда не требуется специально выполнять простое

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

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

эффекта.

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

поэтому в таких случаях препроцессор выдает предупреждающее сообщение.

Однако, это иногда помогает при изменении определения макроса во время

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

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

'#undef'.

Для простого переопределения новое определение долно точно совпадать

с предыдущим значением за исключением двух случаев:

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

Пробелы можно изменять в середине определения (но не в середине

строки). Однако они не могут быть полностью удалены, а также не могут быть

вставлены туда, где их не было вообще.

4.8. Особенности использования макросов

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

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

случаи, которые следует иметь в виду.

4.8.1. Неправильно используемые конструкции

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

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

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

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

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

#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))'.

Свежие статьи
Популярно сейчас
Как Вы думаете, сколько людей до Вас делали точно такое же задание? 99% студентов выполняют точно такие же задания, как и их предшественники год назад. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
5209
Авторов
на СтудИзбе
430
Средний доход
с одного платного файла
Обучение Подробнее