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);
что является одним выражением.
... 1-12. Напишите программу, печатающую гистограмму длин слов из файла ввода. Самое легкое - начертить гистограмму горизон- тально; вертикальная ориентация требует больших усилий. 1.7. Функции. В языке “C” функции эквивалентны подпрограммам или функ- циям в фортране или процедурам в PL/1, паскале и т.д. Функ- ции дают удобный способ заключения некоторой части вычисле- ний в черный ...
... , сложны для понимания и абсолютно непрозрачны, а возможности существенно уступают Форту и Лиспу. В общем, муть и мрак. Вавилонское столпотворение Всякий раз, когда появляется очередной новый язык, о котором говорят, как об «окончательном и безальтернативном», предрекая скорую смерть всех остальных, мне становится смешно. Сам по себе язык в отрыве от среды программирования —малоинтересен, да и все ...
... программе. В данном разделе они перечислены в алфавитном порядке и приводятся с объяснениями. Эти ошибки могут являться следствием случайного затирание памяти программой. Abnormal program termination Аварийное завершение программы Данное сообщение может появляться, если для выполнения программы не может быть выделено достаточного количества памяти. Более подробно оно рассматривается в конце ...
... доступа с записью равной байту. Такие файлы называются двоичными. Файлы прямого доступа незаменимы при написании программ, которые должны работать с большими объемами информации, хранящимися на внешних устройствах. В основе обработке СУБД лежат файлы прямого доступа. Кратко изложим основные положения работы с файлами прямого доступа. 1). Каждая запись в файле прямого доступа имеет свой номер ...
0 комментариев