Фаза препроцессирования происходит после того, как будут конвертированы
триграфы, а физические строки, оканчивающиеся \
будут объединены
в длинные логические, но до замены эскейп-последовательностей в символьных
константах и объединения соседних стринговых литералов.
Любая строка, чьим первым непробельным символом является #
обозначает начало директивы препроцессора. Между #
и
идентификатором директивы могут находится пробелы. Директивы
#include
и #define
занимают одну строку (после
объединения строк оканчивающихся \
), а директивы условной
компиляции охватывают несколько.
Директива препроцессора не заканчивается точкой с запятой.
Директива препроцессора в виде
#
(без какой-либо лексемы в той же строке) ничего не производит и игнорируется.
Директива в виде
#include <имя>
будет искать в местах определённых реализацией заголовочный файл, идентифицируемый именем. Заголовочный файл декларирует набор библиотечных функций и необходимые типы или макро для их использования. Заголовочные файлы обычно предоставляются компилятором или библиотекой для этого компилятора.
Имя не может содержать символ >
. Если заголовочный файл
найден, то директива заменяется содержимым этого файла.
Директива в виде
#include "имя"
будет искать исходный файл идентифицируемый именем. Имя не
может содержать символ "
(двойная кавычка). Если исходный файл
найден, то директива заменяется его содержимым. В противном случае она
обрабатывается как, если бы была использована форма
#include <имя>
Поддерживается также третья форма директивы #include
. Директива в виде
#include лексемы
заставляет произвести макроподстановки (описанные ниже) вместо
лексемы. После этого директива должна соответствовать одной из форм,
либо <
имя>
, либо
"
имя"
, описанных выше (включая
<
и >
или кавычки), и в этом случае #include
будет обрабатываться соответствующим способом.
Подробности о том, как компилятор ищет включаемые файлы см. User's Guide.
Директивы #include
могут быть вложенными. Каждая реализация
может позволять различную степень вложенности, но как минимум 8 уровней.
(Другими словами, исходный файл может включать другой файл, который включает
ещё один файл, и т.д. на глубину восьми файлов.)
Операционная система может ограничивать число одновременно открытых файлов. Подробнее см. соответствующее руководство по операционной системе.
Директива в виде
#if константное-выражение тело #if #endif
вычисляет константное-выражение, и, если оно не равно нулю, то
тело обрабатывается препроцессором. Обработка тела
заканчивается соответствующим #elif
, #else
или
после встречи закрывающего #endif
.
Директива #if
позволяет компилятору и препроцессору обрабатывать
строки исходного кода в соответствии с условием.
Если константное-выражение равно нулю, то тело #if не
обрабатывается, а обрабатываются соответствующие #elif
или
#else
(если они присутствуют). Если нет ни одной из этих
директив, то препроцессор пропускает всё вплоть до #endif
. Любые
директивы препроцессора внутри тела #if
не обрабатываются, но
проверяется, есть ли вложенные директивы #elif
,
#else
или #endif
.
Константное-выражение используется в той же форме, что и в командах,
за исключением того, что значения должны быть целыми (включая символьные
константы). Нельзя использовать операторы приведения типа или
sizeof
, а также константы перечислений. Каждый идентификатор,
который является именем макро, заменяется (как описано ниже), а остальные
идентификаторы заменяются на 0L
. Все значения конвертируются в
длинные целые с использованием обычных арифметических преобразований. После
этого происходит вычисление при помощи окружения трансляции. Любой символ
вычисляется как элемент исходного символьного набора.
В Watcom C/16 и C/32 значения символьных констант одинаковы и в исходном символьном наборе и в наборе времени исполнения.
Можно использовать унарное выражение
defined
идентификатор
или
defined(
идентификатор )
для выяснения того, определён ли идентификатор как макро. Любое имя макро, являющееся частью этого выражения, не расширяется. Предыдущее выражение равно 1, если идентификатор является макро, и — 0 в противном случае.
Как сказано выше, если вычисление константного-выражения
#if
даёт ноль, то препроцессор ищет соответствующий
#elif
. Эта директива означает "else if" (иначе, если), и имеет
форму похожую на #if
:
#elif константное-выражение тело #elif
#elif
можно помещать только внутри тела #if
.
Тело #elif обрабатывается только, если вычисление
константного-выражения даёт ненулевое значение, а
константные-выражения соответствующего #if
и (предшествующего)
#elif
равны нулю. В противном случае тело не обрабатывается, и
препроцессор пропускает всё до соответствующего #elif
или
#else
, или #endif
, если нет ни одной из этих двух
директив.
Директива #else
имеет следующую форму:
#else тело #else
Тело #else обрабатывается только, если константные выражения
соответствующих #if
и #elif
равны нулю. Тело
#else
обрабатывается вплоть до соответствующего
#endif
.
Формой директивы #endif
является
#endif
и обозначает конец #if
.
Далее следуют примеры условного включения строк кода:
#if OPSYS == OS_CMS fn_syntax = "filename filetype fm"; #elif OPSYS == OS_MVS fn_syntax = "'userid.library.type(membername)'"; #elif OPSYS == OS_DOS || OPSYS == OS_OS2 fn_syntax = "filename.ext"; #else fn_syntax = "filename"; #endif
В зависимости от значения макро OPSYS
объекту
fn_syntax
присваивается имя файла, соответствующее синтаксису.
Если OPSYS
не соответствует ни одному из указанных значений, то,
по умолчанию, fn_syntax
присваивается "filename"
.
#if HARDWARE == HW_IBM370 #if OPSYS == OS_CMS escape_cmd = "CMS"; #elif OPSYS == OS_MVS escape_cmd = "TSO"; #else escape_cmd = "SYSTEM"; #endif #else escape_cmd = "SYSTEM"; #endif
В соответствии со значениями макро HARDWARE
и OPSYS
объекту escape_cmd
присваивается определённый стринг. Отступы
перед директивами чётко иллюстрируют вложенность условий и директив.
#ifdef
и #ifndef
Директива #ifdef
используется для проверки того, определён ли
данный идентификатор как макро. Например,
#ifdef xyz
обрабатывает тело #ifdef
только, если идентификатор
xyz
в данный момент является макро. Этот пример эквивалентен
#if defined xyz
или
#if defined( xyz )
Подобно этому директива
#ifndef xyz
эквивалентна
#if !defined xyz
или
#if !defined( xyz )
Директива в виде
#define идентификатор список-подстановок
определяет макро с именем идентификатор. Это частная форма макро называемая «макро подобное объекту», потому что оно используется как объект (в противоположность функции). В любой строке исходного текста лексема, совпадающая с именем макро, заменяется на список-подстановок. Затем лексемы из списка-подстановок сканируются заново для дополнительных макроподстановок.
Например,
#define TABLE_LIMIT 256
определяет макро TABLE_LIMIT
как эквивалент лексемы
256
. Его иногда называют самообъявляющейся константой,
потому что оно предоставляет описывающую лексему для значения, что делает
программу проще для чтения. Использование описательных имён везде, где это
возможно, для улучшения читабельности программы является очень хорошей идеей.
Это также сбережёт время, если одно и то же значение используется в
нескольких местах, т.к. при необходимости, изменения нужно будет делать
только в одном месте.
При использовании сложных макро, подобных объекту, следует быть осторожным. Рассмотрим следующий пример:
#define COUNT1 10 #define COUNT2 20 #define TOTAL_COUNT COUNT1+COUNT2 /* ... */ memptr = malloc( TOTAL_COUNT * sizeof( int ) );
Если размер int
равен 2, то этот вызов malloc
зарезервирует 50 байт памяти вместо ожидаемых 60. Это произойдёт, потому что
после макроподстановки TOTAL_COUNT * sizeof( int )
станет
10+20 * 2
, и в соответствии с приоритетом вычислений умножение
будет сделано первым. Чтобы решить эту проблему, макро
TOTAL_COUNT
необходимо объявит как
#define TOTAL_COUNT (COUNT1+COUNT2)
Директива в виде
#define идентификатор( список-идентификаторов ) список-подстановок
называется «макро, подобное функции», так как оно используется подобно функции. В макроопределении между левой круглой скобкой и идентификатором пробелы не допускаются. В любой строке исходного кода, которая содержит нечто похожее на вызов функции, и имя функции совпадает именем макро подобного функции, а число параметров совпадает с числом идентификаторов в списке-идентификаторов, происходит замена этого вызова на список-подстановок, с заменой идентификаторов в списке-подстановок на аргументы. Если левая круглая скобка, следующая за именем макро, образовалась в результате макроподстановки, то дальнейшие макроподстановки производиться не будут. Если есть имя макро, но за ним не следует левая круглая скобка, то дальнейшие макроподстановки производиться не будут.
Рассмотрим пример:
#define endof( string ) \ (string + strlen( string ))
Обратная косая черта (\
) означает, что эта и следующая строка
должны быть объединены в одну логическую, что будет эквивалентно
#define endof( string ) (string + strlen( string ))
Макро endof
подобное функции можно использовать для получения
указателя на нулевой символ, заканчивающий стринг. Над командой
endptr = endof( ptr );
будет произведена макроподстановка, и она будет транслироваться как
endptr = (ptr + strlen( ptr ));
Обратите внимание, что в этом случае аргумент вычисляется дважды. Если вместо
ptr
будет указана функция StrFn( ptr )
, то она
будет вызвана дважды, потому что после макроподстановки будет следующее:
endptr = (StrFn( ptr ) + strlen( StrFn( ptr ) ));
В наборе лексем, используемых для обозначения аргументов, каждая последовательность лексем, разделённая запятой представляет собой аргумент, если эта запятая не находится между левой и правой круглыми скобками. Когда встретится правая скобка, соответствующая начальной левой, и число аргументов совпадёт с числом идентификаторов в определении макро, то набор считается полным, и производится подстановка.
Например,
#define mymemcpy( dest, src, len ) \ memcpy( dest, src, len ) /* ... */ mymemcpy( destptr, srcptr, (t=0, t=strlen(srcptr)) );
В этом случае параметры dest
, src
и
len
будут заменены на аргументы destptr
,
srcptr
и (t=0, t=strlen(srcptr))
соответственно.
Эту форму макро можно использовать, чтобы закомментировать вызов функции, который используется во время отладки программы. Например,
#define alive( where ) printf( "Нахожусь " where "\n" )
можно заменить на
#define alive( where ) /* */
Вместо этого можно использовать определение
#define alive( where )
Когда модуль или программа будет перекомпилированы с использованием этого
нового определения для alive
, то все вызовы printf
,
сделанные как результат макроподстановки, исчезнут без необходимости удаления
соответствующих строк в каждом модуле.
Директива в форме
#undef идентификатор
отменяет макроопределение для идентификатора. Если макроопределение для идентификатора отсутствует, то никакого сообщения об ошибке не будет.
Возможности препроцессора C по подстановке аргументов очень велики, но могут быть ненадёжными. В следующих разделах они показаны, а также сделана попытка пролить свет на проблемы, которые могут возникнуть.
В строке замены макро, похожего на функцию, за каждым вхождением
#
должен следовать параметр макро. Если это так, то оба, и
#
, и параметр заменяются на стринг состоящий из символов
аргумента, а над аргументом больше не производится никаких подстановок.
Каждый пробельный символ внутри аргумента заменяется на один пробел. Если
аргумент содержит символьные константы или стринговые литералы, то каждая
"
(двойная кавычка) заменяется на \"
, а
\
— на \\
.
В следующей таблице приводятся несколько примеров расширения макро
#define string( parm ) # parm
при использовании его как показано в первой колонке.
Аргумент | После подстановки |
---|---|
string( abc ) | "abc" |
string( "abc" ) | "\"abc\"" |
string( "abc" "def" ) | "\"abc\" \"def\"" |
string( \'/ ) | "\\'/" |
string( f(x) ) | "f(x)" |
Если ##
предшествуют или следуют за параметром в списке
подстановок, то параметр заменяется аргументом без попыток дальнейших
подстановок.
#
не может быть первой или последней лексемой в списке
подстановок.
Допустим определены следующие макро:
#define first "Piece" #define last "of Earth" #define firstlast "Peace on Earth" #define first1 "Peas"
В следующей таблице приводится несколько примеров результата применения макро
#define glue( x, y ) x ## y
как показано в первой колонке. Для примеров, которые занимают несколько строк, каждая строка в колонке «После подстановки» обозначает последовательное расширение макро.
Аргумент | После подстановки |
---|---|
glue( 12, 34 ) | 1234 |
glue( first, 1 ) |
first1 "Peas" |
glue( first, 2 ) | first2 |
glue( first, last ) |
firstlast "Peace on Earth" |
При отсутствии операторов #
либо ##
параметр
заменяется аргументом. Однако до этого аргумент проверяется, нужно ли вместо
него производить ещё какие-нибудь подстановки в соответствии с перечисленными
выше правилами. При этом просматривается только аргумент, но не
какие-либо лексемы, которые могут соседствовать с ним при замене параметра.
Другими словами, если последняя лексема аргумента и первая лексема, следующая
за ней в списке подстановок, образуют правильное макро, то никаких
подстановок такого макро не делается.
Рассмотрим следующие примеры с данными макроопределениями:
#define f(a) a #define g(x) (1+x) #define h(s,t) s t #define i(y) 2-y #define xyz printf #define rcrs rcrs+2
Вызов | После подстановки |
---|---|
f(c) | c |
f(f(c)) |
f(c) c |
f(g(c)) |
f((1+c)) (1+c) |
h("hello",f("there")) |
h("hello","there") "hello" "there" |
f(xyz)("Hello\n") |
f(printf)("Hello\n") printf("Hello\n") |
После того, как все параметры в списке подстановок заменены, результирующий набор лексем просматривается заново для дальнейших подстановок. Если при сканировании встретится вызов макро, которое обрабатывается в данный момент, то оно не заменяется. Дальнейшие подстановки такого макро не могут производиться, пока не будет обрабатываться новый, не имеющий отношения к текущей подстановке, набор лексем.
Рассмотрим примеры, которые используют приведённые ранее макроопределения:
Вызов | После пересмотра |
---|---|
f(g)(r) |
g(r) (1+r) |
f(f)(r) | f(r) |
h(f,(b)) |
f (b) b |
i(h(i,(b))) |
i(i (b)) 2-i (b) |
i(i (b)) | i(2-b) 2-2-b |
rcrs | rcrs+2 |
Другими словами, если встретился видимый вызов макро, и его имя совпадает с именем макро, которое заменяется в данный момент, но вызов получился в результате других подстановок, то он не заменяется. А, если вызов происходит из аргумента макро, то он заменяется.
После всех замен результирующий набор лексем заменяет вызов макро в исходном файле, и файл пересматривается, начиная со списка подстановок. Заменяются все дальнейшие вызовы макро. Однако, если при просмотре списка подстановок с подставленными лексемами встретится видимый вызов макро, которое только что было заменено, то это имя макро не заменяется. Видимый вызов макро будет снова заменён только тогда, когда встретится новый вызов этого макро, не связанный с данной заменой.
Если список подстановок лексем внешне похож на директиву препроцессора, то препроцессор их не обрабатывает.
Макроопределение сохраняется, пока не будет удалено (при помощи
#undef
) или до конца модуля.
Следующие примеры даются в стандарте ANSI языка C и приведены здесь для демонстрации всех способов, которыми заменяются макро. Для того, чтобы лучше проиллюстрировать процесс замены, он показан в несколько стадий.
Первый набор примеров демонстрирует правила создания стринговых литералов (с
использованием оператора #
) и склеивания лексем (с
использованием оператора ##
). В примерах используются следующие
определения:
#define str(s) # s #define xstr(s) str(s) #define debug(s, t) printf("x" # s "= %d, x" # t "= %s", x ## s, x ## t) #define INCFILE(n) vers ## n /* comment */ #define glue(a, b) a ## b #define xglue(a, b) glue(a, b) #define HIGHLOW "hello" #define LOW LOW ", world"
Сделаны показанные ниже замены. В окончательном результате соседние стринговые литералы соединены вместе для образования одной строки. На самом деле этот шаг не происходит на стадии препроцессора, а приведён для ясности.
debug( 1, 2 ); printf( "x" "1" "= %d, x" "2" "= %s", x1, x2 ); printf( "x1= %d, x2= %s", x1, x2 ); fputs(str(strncmp("abc\0d", "abc", '\4') /* этого не будет */ == 0) str(: @@\n), s); fputs("strncmp(\"abc\\0d\", \"abc\", '\\4') == 0" ": @@\n", s); fputs("strncmp(\"abc\\0d\", \"abc\", '\\4') == 0: @@\n", s); #include xstr(INCFILE(2).h) #include xstr(vers2.h) #include str(vers2.h) #include "vers2.h"
(затем директива заменяется на содержимое файла)
glue(HIGH, LOW) HIGHLOW "hello" xglue(HIGH, LOW) xglue(HIGH, LOW ", world") glue( HIGH, LOW ", world") HIGHLOW ", world" "hello" ", world" "hello, world"
Следующие примеры демонстрируют правила переопределения и повторного просмотра макроподстановок. Используются следующие определения:
#define x 3 #define f(a) f(x * (a)) #undef x #define x 2 #define g f #define z z[0] #define h g(~ #define m(a) a(w) #define w 0,1 #define t(a) a
Сделаны следующие подстановки:
f(y+1) + f(f(z)) % t(t(g)(0) + t)(1) f(x * (y+1)) + ... f(2 * (y+1)) + f(f(z)) % t(t(g)(0) + t)(1) ... + f(f(x * (z))) % ... ... + f(f(2 * (z))) % ... ... + f(x * (f(2 * (z)))) % ... ... + f(2 * (f(2 * (z)))) % ... ... + f(2 * (f(2 * (z[0])))) % t(t(g)(0) + t)(1) ... % t(g(0) + t)(1) ... % t(f(0) + t)(1) ... % t(f(x * (0)) + t)(1) ... % t(f(2 * (0)) + t)(1) f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1)
Ещё один пример:
g(2+(3,4)-w) | h 5) & m(f)^m(m) f(2+(3,4)-w) | ... f(2+(3,4)-0,1) | ... f(x * (2+(3,4)-0,1)) | ... f(2 * (2+(3,4)-0,1)) | h 5) & ... ... | g(~ 5) & ... ... | f(~ 5) & ... ... | f(x * (~ 5)) & ... ... | f(2 * (~ 5)) & m(f)^... ... & f(w)^... ... & f(0,1)^... ... & f(x * (0,1))^... ... & f(2 * (0,1))^m(m) ... ^m(w) f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1)
Макроопределение сохраняется, пока не будет явно удалено (при помощи
директивы #undef
) либо не закончится компиляция исходного файла.
Если макро удалено, то его можно переопределить по-другому, либо также. Если
во время макроподстановки встретится имя макро, которое было определено,
удалено, а затем определено снова, то используется текущее (более новое)
макроопределение, а не то, которое использовалось во время определения
обрабатываемой макроподстановки.
Рассмотрим пример:
#define MAXVAL 1000 #define g(x) CheckLimit( x, MAXVAL ) #undef MAXVAL #define MAXVAL 200 g( 10 );
Этот вызов макро заменится на
CheckLimit( 10, 200 );
Макро, которое определено, может быть переопределено (без удаления) только, если новое определение имеет тот же список подстановок, что и начальное. Все лексемы препроцессора и в начальном, и в новом списке подстановок должны иметь одинаковый порядок и написание. Количество лексем также должно быть одинаково. Число пробелов между лексемами не имеет значения, но не тогда, когда одно определение не содержит пробелы, а другое содержит.
Следующие примеры демонстрируют верные переопределения макро:
#define OBJ_LIKE (1-1) #define OBJ_LIKE /******/ (1-1) /****/ #define FN_LIKE(a) ( a ) #define FN_LIKE( a ) ( /******/ \ a /******* \ */ )
Следующие примеры демонстрируют неправильные переопределения тех же макро:
#define OBJ_LIKE (0)
Разная последовательность лексем.
#define OBJ_LIKE (1 - 1)
Разные разбивки (в определении выше пробелов нет)
#define FN_LIKE(b) ( a )
Параметр имеет другое имя и используется по-другому.
#define FN_LIKE(b) ( b )
Параметр имеет другое имя.
Директива в виде
#line номер
заменяет номер текущей строки в исходном файле указанным номером.
Директива в виде
#line номер стринг
заменяет номер строки, как и выше, а также заменяет имя исходного файла, который обрабатывается в данный момент на имя содержащееся в стринге.
Если директива не распознана как одна из описанных выше, то производится макроподстановка (если возможно) лексем в строке, и делается новая попытка. Если директива всё ещё не соответствует одной из двух форм, то сообщается об ошибке.
Директива в виде
#error лексемы
заставляет компилятор показать диагностическое сообщение содержащее лексемы в директиве.
Директива в виде
#pragma лексемы
информирует компилятор о некоторых аспектах компиляции зависящих от реализации.
Подробности о директиве #pragma
см. «User's Guide».
Следующие имена макро зарезервированы компилятором:
__DATE__
Дата трансляции исходного файла (стринговый литерал). Дата представлена в виде «Mmm dd yyyy», где:
Mmm
представляет собой месяц, один из
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
dd
день месяца. Первый символ будет пробелом, если день меньше 10.
yyyy
год.
Если компилятор не может определить текущую дату, то будет предоставлена какая-либо другая дата.
В Watcom C/16 и C/32 текущая дата доступна всегда.
__FILE__
Имя текущего исходного файла (стринговый литерал). Имя можно изменить,
используя директиву #line
.
__LINE__
Номер строки в текущем исходном файле (десятичная константа). Номер строки
можно изменить, используя директиву #line
.
__STDC__
Десятичная константа 1, означающая, что компилятор соответствует соглашениям стандарта.
__TIME__
Время трансляции исходного файла (стринговый литерал). Имеет вид
«hh:mm:ss»
, с лидирующими нулями у значений меньших 10. Если
компилятор не может определить текущее время, то будет предоставлено
какое-либо другое время.
В Watcom C/16 и C/32 текущее время доступно всегда.
Любые другие макро предопределённые компилятором будут начинаться со знака
подчёркивания (_
). Предопределённые макро и имена нельзя удалить
(при помощи #undef
) или переопределить (при помощи
#define
).
Компиляторы Watcom C/16 и C/32 дополнительно предоставляют следующие предопределённые макро для описания используемой модели памяти:
__COMPACT__
Используется компактная модель памяти.
__FLAT__
Используется «плоская» модель памяти для процессора 80386. Все сегментные регистры указывают на один сегмент.
__HUGE__
Используется огромная модель памяти.
__LARGE__
Используется большая модель памяти.
__MEDIUM__
Используется средняя модель памяти.
__SMALL__
Используется малая модель памяти.
Компиляторы Watcom C/16 и C/32 дополнительно предоставляют следующие макро для описания целевой операционной системы:
__DOS__
Программа компилируется для использования под управлением операционной системы DOS.
__NETWARE_386__
Программа компилируется для использования под управлением операционной системы Novell Netware 386.
__NT__
Программа компилируется для использования под управлением операционной системы Windows NT.
__OS2__
Программа компилируется для использования под управлением операционной системы OS/2.
__QNX__
Программа компилируется для использования под управлением операционной системы QNX.
__WINDOWS__
Программа компилируется для использования с Microsoft Windows.
__WINDOWS_386__
Программа компилируется для использования с Microsoft Windows с интерфейсом Watcom 32-bit Windows.
Кроме этого, компилятор Watcom C/16 предоставляет следующее макро:
__CHEAP_WINDOWS__
Программа компилируется для использования с Microsoft Windows с опцией компилятора «zW».
Кроме этого, компиляторы Watcom C/16 и C/32 предоставляют следующие макро:
__CHAR_SIGNED__
Программа компилируется с использованием опции компилятора «j». Тип
char
по умолчанию — знаковый.
__FPI__
Программа компилируется с использованием встроенных инструкций по обработке чисел с плавающей точкой.
__INLINE_FUNCTIONS__
Программа компилируется с использованием опции компилятора «oi».
__WATCOMC__
Используется компилятор Watcom C/16 или Watcom C/32. Значение макро — номер версии компилятора помноженный на 100.
__386__
Программа компилируется для процессора 80386 с использованием компилятора Watcom C/32.
Кроме этого компиляторы Watcom C/16 и C/32 предоставляют следующие
предопределённые макро для совместимости с компилятором Microsoft C, хотя
большинство этих макро не начинаются с символа подчёркивания
(_
):
MSDOS
Программа компилируется для использования под управлением операционной системы DOS.
_M_IX86
Программа компилируется для конкретной целевой архитектуры. Макро равно
значению опции компилятора (-0, -1, -2, -3, -4, -5, и т.д.) помноженному на
100. Если указана опция компилятора «-5» (оптимизация для процессора
Pentium), то значение макро _M_IX86
будет равно 500.
M_I86
Программа компилируется для процессора Intel 80x86.
M_I386
Программа компилируется для процессора Intel 80386.
M_I86CM
Используется компактная модель памяти.
M_I86HM
Используется огромная модель памяти.
M_I86LM
Используется большая модель памяти.
M_I86MM
Используется средняя модель памяти.
M_I86SM
Используется малая модель памяти.
NO_EXT_KEYS
Программа компилируется для соответствия ANSI/ISO с использованием опции компилятора «za» (без дополнительных ключевых слов).
offsetof
Макро
offsetof( тип, член );
расширяется в константное выражение типа size_t
. Значение
выражения — смещение члена в байтах от начала структуры типа
тип. Член не может быть битовым полем.
Для использования этого макро нужно включить заголовочный файл
<stddef.h>
.
NULL
Макро NULL
расширяется в константу null-указатель, чьё
значение представляет указатель, который не указывает на что-либо.В качестве
null-указателя рекомендуется использовать NULL
вместо
0
.Для использования этого макро нужно включить заголовочный файл
<stddef.h>
.