В макросах обоих видов замещающая последовательность лексем повторно просматривается на предмет обнаружения там новых define -имен. Однако, если некоторый идентификатор уже был заменен в данном расширении, повторное появление такого идентификатора не вызовет его замены.
Если полученное расширение начинается со знака #, оно не будет воспринято как директива препроцессора.
В ANSI-стандарте процесс макрорасширения описан более точно, чем в первом издании книги. Наиболее важные изменения касаются введения операторов # и ##, которые предоставляют возможность осуществлять расширения внутри строк и конкатенацию лексем. Некоторые из новых правил, особенно касающиеся конкатенации, могут показаться несколько странными. (См. приведенные ниже примеры.)
Описанные возможности можно использовать для показа смысловой сущности констант, как, например, в
#define TABSIZE 100
int table[TABSIZE];
Определение
#define ABSDIFF(a,b) ((a)>(b) ? (a)-(b) : (b)-(a))
задает макрос, возвращающий абсолютное значение разности его аргументов. В отличие от функции, делающей то же самое, аргументы и возвращаемое значение здесь могут иметь любой арифметический тип и даже быть указателями. Кроме того, аргументы, каждый из которых может иметь побочный эффект, вычисляются дважды: один раз - при проверке, другой раз - при вычислении результата.
Если имеется определение
#define tempfile(dir) #dir "/%s"
то макровызов tempfile(/usr/tmp) даст в результате
"/usr/tmp" "/%s"
Далее эти две строки превратятся в одну строку. По макросу
#define cat(x,y) x ## y
вызов cat(var, 123) сгенерирует var123 . Однако cat (cat (1,2),3) не даст желаемого, так как оператор ## воспрепятствует получению правильных аргументов для внешнего вызова cat . В результате будет выдана следующая цепочка лексем:
cat ( 1 , 2 )3
где )3 (результат "склеивания" последней лексемы первого аргумента с первой лексемой второго аргумента) не является правильной лексемой. Если второй уровень макроопределения задан в виде
#define xcat(x,y) cat(x,y)
то никаких коллизий здесь не возникает;
xcat(хсat(1, 2), 3)
в итоге даст 123, поскольку сам xcat не использует оператора ##.
Аналогично сработает и ABSDIFF(ABSDIFF(a, b), c), и мы получим правильный результат.
Управляющая строка
#include < имя-файла >
заменяется на содержимое файла с именем имя-файла . Среди символов, составляющих имя-файла , не должно быть знака > и символа новой строки. Результат не определен, если имя-файла содержит любой из символов ", ', \ или пару символов /*. Порядок поиска указанного файла зависит от реализации.
Подобным же образом выполняется управляющая строка
#include " имя-файла "
Сначала поиск осуществляется по тем же правилам, по каким компилятор ищет первоначальный исходный файл (механизм этого поиска зависит от реализации), а в случае неудачи осуществляется методом поиска, принятым в #include первого типа. Результат остается неопределенным, если имя файла содержит ", \ или /*; использование знака > разрешается.
Наконец, директива
#include последовательность-лексем
не совпадающая ни с одной из предыдущих форм, рассматривает последовательность лексем как текст, который в результате всех макроподстановок должен дать #include <...> или #include "..." . Сгенерированная таким образом директива далее будет интерпретироваться в соответствии с полученной формой.
Файлы, вставляемые с помощью #include , сами могут содержать в себе директивы #include .
A12.5. Условная компиляция
Части программы могут компилироваться условно, если они оформлены в соответствии со следующим схематично изображенным синтаксисом:
условная - конструкция -препроцессора :
if-строка текст elif-части else-часть необ #endif
if-строка :
#if константное-выражение
#ifdef идентификатор
#ifndef идентификатор
elif-части :
elif-строка текст
elif-части необ
elif-строка :
#elif константное-выражение
else-часть :
else-строка текст
else-строка :
#else
Каждая из директив ( if -строка, elif -строка, else -строка и #endif ) записывается на отдельной строке. Константные выражения в #if и последующих строках #elif вычисляются по порядку, пока не обнаружится выражение с ненулевым (истинным) значением; текст, следующий за строкой с нулевым значением, выбрасывается. Текст, расположенный за директивой с ненулевым значением, обрабатывается обычным образом. Под словом " текст " здесь имеется в виду любая последовательность строк, включая строки препроцессора, которые не являются частью условной структуры; текст может быть и пустым. Если строка #if или #elif с ненулевым значением выражения найдена и ее текст обработан, то последующие строки #elif и #else вместе со своими текстами выбрасываются. Если все выражения имеют нулевые значения и присутствует строка #else , то следующий за ней текст обрабатывается обычным образом. Тексты "неактивных" ветвей условных конструкций, за исключением тех, которые заведуют вложенностью условных конструкций, игнорируются.
Читать дальше