Директива #define
Синтаксис:
#define <идентификатор> <текст>
#define <идентификатор> <список параметров> <текст>
Директива #define заменяет все вхождения <идентификатора> в исходном файле на <текст>, следующий в директиве за <идентификатором>. Этот процесс называется макроподстановкой, <идентификатор> заменяется лишь в том случае, если он представляет собой отдельную лексему. Например, если <идентификатор> является частью строки или более длинного идентификатора, он не заменяется. Если за <идентификатором> следует <список параметров>, то директива определяет макроопределение с аргументами.
<
<Текст> может быть опущен. В этом случае все экземпляры <идентификатора> будут удалены из исходного текста программы. Тем не менее, сам <идентификатор> рассматривается как определенный и при проверке директивой #if дает значение 1 (смотри раздел 7.4.1).
<Список параметров>, если он задан, содержит один или более идентификаторов, разделенных запятыми. Идентификаторы в списке должны отличаться друг от друга. Их область действия ограничена макроопределением, в котором они заданы. Список должен быть заключен в круглые скобки. Имена формальных параметров в < тексте> отмечают позиции, в которые должны быть подставлены фактические аргументы макровызова. Каждое имя формального параметра может появиться в <тексте> произвольное число раз.
В макровызове следом за <идентификатором> записывается в круглых скобках список фактических аргументов, соответствующих формальным параметрам из <списка параметров>. <Текст> модифицируется путем замены каждого формального параметра на соответствующий фактический аргумент. Списки фактических аргументов и формальных параметров должны содержать одно и то же число элементов.
Примечание. Не следует путать подстановку аргументов в макроопределение с передачей аргументов функции. Подстановка в препроцессоре носит чисто текстовый характер. Никаких вычислений или преобразований типа при этом не производится.
Выше уже говорилось, что макроопределение может содержать более одного вхождения данного формального параметра. Если формальный параметр представлен выражением с побочным эффектом, то это выражение будет вычисляться более одного раза, а вместе с ним каждый раз будет возникать и побочный эффект. Результат выполнения в этом случае может быть ошибочным.
Внутрь <текста> в директиве #define могут быть вложены имена других макроопределений или констант. Их расширение производится лишь при расширении <идентификатора> этого <текста>, а не при его определении директивой #define. Это надо учитывать, в частности, при взаимодействии вложенных именованных констант и макроопределений с директивой #undef: к моменту расширения содержащего их текста они могут уже оказаться отменены директивой #undef.
После того как выполнена макроподстановка, полученная строка вновь просматривается для поиска других имен констант и макроопределений. При повторном просмотре не принимается к рассмотрению имя ранее произведенной макроподстановки. Поэтому директива
#define х х
не приведет к зацикливанию препроцессора.
Примеры.
/* пример 1 */
#define WIDTH 80
#define LENGTH (WIDTH + 10)
/* пример 2 */
#define FILEMESSAGE «Попытка создать файл
не удалась из-за нехватки дискового пространства»
/* пример 3 */
#define REG1 register
#define REG2 register
#define REG3
/* пример 4 */
#define MAX(x, y)((x)>(у)) ? (x) : (у)
/* пример 5 */
#define MULT(a, b) ((a)*(b))
В первом примере идентификатор WIDTH определяется как целая константа со значением 80, а идентификатор LENGTH — как текст (WIDTH + 10). Каждое вхождение идентификатора LENGTH в исходный файл будет заменено на текст (WIDTH + 10), который после расширения идентификатора WIDTH превратится в выражение (80 + 10). Скобки, окружающие текст (WIDTH + 10), позволяют избежать ошибок в операторах, подобных следующему:
var = LENGTH * 20;
После обработки препроцессором оператор примет вид:
var = (80 + 10)* 20;
Значение, которое присваивается var, равно 1800. В отсутствие скобок в макроопределении оператор имел бы следующий вид:
var = 80 + 10*20;
Значение var равнялось бы 280, поскольку операция умножения имеет более высокий приоритет, чем операция сложения.
Во втором примере определяется идентификатор FILEMESSAGE. Его определение продолжается на вторую строку путем использования символа обратный слэш непосредственно перед нажатием клавиши ENTER.
В третьем примере определены три идентификатора, REG1, REG2, REG3. Идентификаторы REG1 и REG2 определены как ключевые слова register. Определение REG3 опущено и, таким образом, любое вхождение REG3 будет удалено из исходного файла. В разделе 7.4.1 приведен пример, показывающий, как эти директивы могут быть использованы для задания класса памяти register наиболее важным переменным программы.
В четвертом примере определяется макроопределение МАХ. Каждое вхождение идентификатора МАХ в исходном файле заменяется на выражение ((x)>(у))?(x):(у), в котором вместо формальных параметров х и у подставлены фактические. Например, макровызов
МАХ(1,2)
заменится на выражение
((1)>(2))?(1):(2)
а макровызов
MAX(i, s[i])
заменится на выражение
((i)>(s(i]))?(i):(s(i])
Обратите внимание на то, что в этом макроопределении аргументы с побочными эффектами могут привести к неверным результатам. Например, макровызов
MAX(i, s[i++])
заменится на выражение
((i)>(s[i++]))?(i):(s[i++])
Операнды операции > могут быть вычислены в любом порядке, а значение переменной i зависит от порядка вычисления. Поэтому результат выражения непредсказуем. Кроме того, возможна ситуация, когда переменная i будет инкрементирована дважды, что, вероятно, не требуется.
В пятом примере определяется макроопределение MULT. Макровызов MULT(3,5) в тексте программы заменяется на (3)*(5). Круглые скобки, в которые заключаются фактические аргументы, необходимы в тех случаях, когда аргументы макроопределения являются сложными выражениями. Например, макровызов
MULT(3+4,5+6)
заменится на (3+4)*(5+6), что равняется 76. В отсутствие скобок результат подстановки 3+4*5+6 был бы равен 29.
Поделитесь на страничке Следующая глава >it.wikireading.ru
Директива #undef
Синтаксис:
#undef <идентификатор>
Директива #undef отменяет действие текущего определения #define для <идентификатора>. Чтобы отменить макроопределение посредством директивы #undef, достаточно задать его <идентификатор>. Задание списка параметров не требуется.
Не является ошибкой применение директивы #undef к идентификатору, который ранее не был определен (или действие его определения уже отменено). Это может использоваться для гарантии того, что идентификатор не определен.
Директива #undef обычно используется в паре с директивой #define, чтобы создать область исходной программы, в которой некоторый идентификатор определен.
Пример:
#define WIDTH 80
#define ADD(X, Y) (X)+(Y)
#undef WIDTH
#undef ADD
В этом примере директива #undef отменяет определение именованной константы WIDTH и макроопределения ADD. Обратите внимание на то, что для отмены макроопределения задается только его идентификатор.
Поделитесь на страничке Следующая глава >it.wikireading.ru
#define
d являются глобальными в том смысле, что они не соответствуют нормальным правилам области видимости C. Текстовая подстановка из макроса будет применяться (почти) везде, где имя макроса появляется после его #define
. (Известны исключения, если имя макроса является частью комментария или частью строкового литерала.)
Если вы определите макрос в заголовочном файле, то любой файл, который #include
#undef
его после #undef
. В вашем примере file2.c
не знает о макросе TEST
. Как бы он узнал, чтобы получить #define
из file1.c
? По волшебству? Поскольку макросы выполняют текстовую подстановку в исходном коде, их нет в созданных объектных файлах. Поэтому file2.c
должен знать само это правило подстановки, и если вы хотите, чтобы оно использовалось совместно несколькими файлами, этот #define
должен находиться в общем заголовочном файле, который ваши .c
файлы #include
.
Если вы спрашиваете конкретно о том, сколько из #ifdef
которое вы видите в библиотеках, работают, многие из них, вероятно, сверяются с предопределенными именами макросов, предоставленными средой компиляции. Например, компилятор C99 определяет макрос __STDC_VERSION__
который определяет языковую версию; компилятор Microsoft определяет макрос _MSC_VER
. (Часто эти предопределенные макросы начинаются с начальных подчеркиваний, поскольку эти имена зарезервированы для компилятора.)
Кроме того, большинство компиляторов позволяют определять простые макросы в качестве аргументов командной строки. Например, вы можете скомпилировать свой код с помощью gcc -DNDEBUG file1.c
для компиляции file.c
с NDEBUG
определенным для отключения assert
s.
qaru.site
Практическая причина использования константы в отличие от определения заключается в том, что вы можете делать прямые сравнения (используя ==) вместо использования isEqual:
. Рассмотрим:
NSString * const kSomeStringConstant = @"LongStringConstantIsLong";
...
[someArray addObject:kSomeStringConstant];
if ([someArray lastObject] == kSomeStringConstant)
{
...
}
Это будет работать, поскольку сравнение ==
будет сравнивать идентичные константные указатели с одним объектом NSString
. Используя #define
, однако:
#define STRING_CONSTANT @"MacrosCanBeEvil";
...
[SomeArray addObject:STRING_CONSTANT]; // a new const `NSString` is created
if ([someArray lastObject] == STRING_CONSTANT) // and another one, here.
{
...
}
Это не сработает, поскольку две строки будут иметь уникальные указатели. Чтобы эффективно сравнивать их, вам нужно будет выполнить сравнение по каждому символу с помощью isEqual:
if ([[someArray lastObject] isEqual:STRING_CONSTANT])
{
...
}
Это может быть гораздо более дорогостоящим с точки зрения времени выполнения, чем простое сравнение ==
.
Другой мотивацией может быть размер самого исполняемого файла. #defined constant будет фактически отображаться на месте, где бы она не использовалась в коде. Это может означать, что строка отображается много раз в вашем исполняемом файле. Напротив, константа должна (с современными компиляторами) быть определена только один раз, и все дальнейшие применения будут ссылаться на указатель на это одно определение.
Теперь, прежде чем кто-либо кричит на меня о преждевременной оптимизации, учтите, что два подхода почти идентичны с точки зрения реализации, но метод указателя const намного превосходит по размеру кода и времени выполнения.
qaru.site