Содержание

Препроцессор

Препроцессор, как следует из его названия, является частью компилятора C, которая обрабатывает некоторые директивы в исходных файлах перед фактической компиляцией. Конкретнее, препроцессор позволяет:

Фаза препроцессирования происходит после того, как будут конвертированы триграфы, а физические строки, оканчивающиеся \ будут объединены в длинные логические, но до замены эскейп-последовательностей в символьных константах и объединения соседних стринговых литералов.

Любая строка, чьим первым непробельным символом является # обозначает начало директивы препроцессора. Между # и идентификатором директивы могут находится пробелы. Директивы #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

Компиляторы 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>.