Перейти к содержимому

C define как в c

  • автор:

#define

Директива #define определяет идентификатор и последовательность символов, которой будет за­мещаться данный идентификатор при его обнаружении в тексте программы. Идентификатор так­же называется именем макроса, а процесс замещения называется подстановкой макроса. Стандар­тный вид директивы следующий:

#define имя_макроса последовательность_символов

Обратим внимание, что в данном операторе отсутствует точка с запятой. Между идентификато­ром и последовательностью символов может быть любое число пробелов. Макрос завершается только переходом на новую строку.

Например, если необходимо использовать TRUE для значения 1, a FALSE для 0 то можно объявить следующие два макроса:

#define TRUE 1
#define FALSE 0

В результате, если компилятор обнаружит в тексте программы TRUE или FALSE, то он заменит их на 1 и 0 соответственно. Например, следующая строка выводит на экран «0 1 2»:

printf («%d %d %d», FALSE, TRUE, TRUE + 1);

В случае, если макрос определен, он может использоваться для определения других макросов. Например, следующий код сопоставляет с именами ONE, TWO и THREE их численные значения:

#define ONE 1
#define TWO ONE + ONE
#def ine THREE ONE + TWO

В результате макроподстановки идентификаторы замещаются указанными строками. Если не­обходимо определить стандартное сообщение об ошибке, то можно написать что-то вроде следу­ющего:

#define E_MS «Standart error on input.\n»
/*. */
printf(E_MS);

Если компилятор обнаруживает идентификатор E_MS, то он замещает его строкой «Standart error on input.» На самом деле компилятор увидит оператор в виде

printf(«Standart error on input.\n»);

Если идентификатор находится в строке, то подстановка не происходит. Например:

#define XYZ this is a test
/*. */
printf(«XYZ»);

выведет не «this is a test», a «XYZ».

Если строка не вмещается в одной строке, то ее можно продолжить на следующей строке, поместив в конце строки обратный слэш, как показано в следующем примере:

#define LONG_STRING «This is a very long» \
string that is used as an example.»

Программисты, пишущие на С, часто используют заглавные буквы для определения идентифика­торов. Данное соглашение помогает любому человеку, читающему программу, бросив на нее один взгляд, узнать, что он имеет дело с макросом. Также вce #define лучше помещать в начале файла или вообще в отдельный заголовочный файл.

Очень часто макросы используют для определения «магических чисел», используемых в про­грамме. Например, программа может определять массив и иметь несколько процедур для работы с ним. Вместо того, чтобы жестко кодировать размер массива, лучше определить макрос, соответ­ствующий размеру массива, и использовать его в тех местах, где необходимо использование раз­мера. Таким образом, если необходимо изменить размер массива, единственное, что требуется сделать, — это изменить оператор #define и перекомпилировать программу. Везде, где использо­вался данный макрос, произойдут автоматические изменения. Рассмотрим пример:

#define MAX_SIZE 100
/*. */
float balance[MAX_SIZE];
/*. */
float temp[MAX_SIZE];

Для изменения размеров обоих массивов просто изменим определение MAX_SIZE.

Директива #define имеет еще одну возможность: макрос может иметь аргументы. Каждый раз при встрече такого макроса аргументы макроса будут замещаться реальными аргументами про­граммы. Такой тип макроса называется макрос типа функция. Например:

#include
#define MIN(a,b) ((a) <(b)) ? (a) : (b)
int main(void)
int x, y;
x = 10; у = 20;
printf(«The minimum is: %d», MIN(x, y) );
return 0;
>

При компиляции программы вместо MIN(a, b) подставляется указанное выражение, причем вме­сто фиктивных параметров а и b подставляются реальные х и у. Таким образом, в результате подстановки функция printf() примет следующий вид:

Надо быть осторожным при определении макросов, получающих аргументы, или можно полу­чить несколько неожиданные результаты. Например, рассмотрим следующую короткую програм­му, использующую макрос для определения четности значения:

/* программа выдает неправильный результат */
#include
#define EVEN(a) a%2==0 ? 1 : 0
int main(void)
if (EVEN(9+1) ) printf(«is even»);
else printf («is odd»);
return 0;
>

Из-за способа подстановки данная программа работает неправильно. В результате компиляции программы EVEN(9 + 1) расширится до

9 + 1% 2 == 0 ? 1 : 0

Как известно, оператор взятия по модулю имеет более высокий приоритет, чем оператор сло­жения. Это означает, что сначала выполнится взятие по модулю с числом 1, а затем результат прибавится к 9, что, естественно, не может быть равно 0. Для устранения данной проблемы сле­дует заключить а в макросе EVEN в круглые скобки, как показано в следующей правильной вер­сии программы:

#include
#define EVEN(a) (a)%2==0 ? 1 : 0
int main(void)
if(EVEN(9 + 1) ) printf(«is even»);
else printf(«is odd»);
return 0;
>

Обратим внимание, что 9+1 вычисляется до взятия по модулю. В целом заключение параметров макроса в скобки — это достаточно хорошая идея, и она позволяет избежать множества проблем.
Использование макроподстановок вместо реальных функций имеет одно большое преимуще­ство — существенно увеличивается скорость работы программы, поскольку нет необходимости тратить время на вызов функции и возврат из нее. Тем не менее, за данное увеличение скорости работы следует платить увеличением размера исполнимого кода программы, поскольку програм­ма вынуждена дублировать код макроса.

Использование defined

Помимо #ifdef существует второй способ узнать, определен ли макрос. В сочетании с директивой #if можно использовать оператор времени компиляции defined. Оператор defined имеет следующий вид:

Если имя_макроса определено, то выражение становится истинным. В противном случае оно ста­новится ложным. Например, для того, чтобы узнать, определен ли макрос MYFILE, можно ис­пользовать одну из двух команд препроцессора:

#if defined MYFILE
или
#ifdef MYFILE

Перед defined можно также поместить ! для изменения состояния. Например, следующий фраг­мент компилируется, только если DEBUG не определен.

#if ! defined DEBUG
printf («Final version!\n»);
#endif

C define как в c

В работе препроцессора «условием» называется директива, при выполнении которой часть программы игнорируется во время компиляции после проверки некоторых условий. В С препроцессоре в условии могут использоваться как арифметические выражения, так и имена определенных макросов. Условие в С препроцессоре в некоторых аспектах имеет сходство с конструкцией ‘if’ языка С, но важно понимать их отличия. Условие в конструкции ‘if’ проверяется при выполнении программы. Ее целью служит изменение хода программы в зависимости от обрабатываемых данных. Условие в препроцессоре проверяется при компиляции программы. Оно используется для включения в программу различных частей кода в зависимости от условий, установленных при ее компиляции.

Существует три основных причины для применения условий. Для выполнения программы на различных платформах может потребоваться разный исходный код. В некоторых случаях, программа, написанная для одной операционной системы, будет некорректно работать в другой операционной системе. В подобных ситуациях недостаточно уклонения от выполнения ненужных процедур. Если программа их содержит, то часто случается, что невозможно скомпоновать и запустить программу. При использовании условной компиляции, неиспользуемый код может быть исключен из программы. Иногда требуется скомпилировать один исходный файл в две разные программы. Случается, что различия в программах заключаются в том, что одна из них выполняет постоянную, занимающую много времени, обработку данных или выдает значения этих данных для отладки, в то время как другая не делает этого. Применение условий, где проверка выдает заведомо ложный результат, используется для исключения кода из программы, который может являться одним из видов комментариев для ссылки на него в будующем. В большинстве простых программ, предназначенных для выполнения только на одном компьютере, условия обычно не используются.

Условие в С препроцессоре начинается с директивы условия: ‘#if’, ‘#ifdef’ или ‘#ifndef’. Далее рассматривается только директива ‘#if’.

Директива ‘#if’

Простейшая форма использования директивы ‘#if’ рассмотрена ниже.

#if EXPRESSION CONTROLLED TEXT #endif /* EXPRESSION */

  • Целые константы, которые рассматриваются как тип ‘long’ или ‘unsigned long’.
  • Символьные константы, которые интерпретируются в соответствии с набором символов и в зависимости от компьютера и операционной системы, на которой установлен препроцессор. Для таких констант GNU С препроцессор использует тип данных ‘char’. Поэтому являются ли коды некоторых символов отрицательными значениями, определяется С компилятором, который использовался для компиляции препроцессора. Если тип ‘char’ является знаковым, то символы, значения которых достаточно велики для установки знакового бита, рассматриваются как отрицательные значения. В противном случае тип ‘char’ является всегда положительным значением.
  • Арифметические операции сложения, вычитания, умножения, деления, операции с битами, сдвиги, сравнения, а также логические операции (‘&&’ и ‘||’).
  • Идентификаторы, не являющиеся макросами и рассматриваемые как нулевое значение.
  • Макровызовы. Перед вычислением значения выражения сначала производится макроподстановка.

Текст, находящийся внутри условной конструкции, может включать директивы препроцессора, которые обрабатываются при выполнении требуемых условий. Текст может также содержать и другие условные конструкции. Однако директивы ‘#if’ и ‘#endif’ должны образовывать единую конструкцию.

Директива ‘#else’

Директива ‘#else’ может использоваться в условной конструкции для предоставления альтернативного кода программы втом случае, если условие ложно. Вот как это выглядит:

#if EXPRESSION TEXT-IF-TRUE #else /* Not EXPRESSION */ TEXT-IF-FALSE #endif /* Not EXPRESSION */

Если значение EXPRESSION является ненулевым и используется код TEXT-IF-TRUE, то директива ‘#else’ рассматривается как ложное условие и код TEXT-IF-FALSE игнорируется. И наоборот, если условие ‘#if’ — ложно, то включается код TEXT-IF-FALSE.

Директива ‘#elif’

Обычное применение однородных условий связано с проверкой более чем двух возможных вариантов. Например:

#if X == 1 . #else /* X != 1 */ #if X == 2 . #else /* X != 2 */ . #endif /* X != 2 */ #endif /* X != 1 */

Дополнительная директива ‘#elif’ позволяет это сократить как рассмотрено ниже.

#if X == 1 . #elif X == 2 . #else /* X != 2 and X != 1*/ . #endif /* X != 2 and X != 1*/

Директива ‘#elif’ означает «иначе если» («else if»). Также как и ‘#else’, она помещается в середину конструкции ‘#if’-‘#endif’ и подразделяет ее. Ей не требуется наличия собственной директивы ‘#endif’. Также как и ‘#if’, директива ‘#elif’ включает в себя тестируемое выражение.

Текст, следующий за директивой ‘#elif’ включается только в том случае, когда значение изходящей директивы ‘#if’ — ложно, а условие ‘#elif’ — верно. В одной конструкции ‘#if’-‘#endif’ может использоваться более чем одна директива ‘#elif’. Текст после директивы ‘#elif’ включается только в том случае, когда условие ‘#elif’ — верно и находится после условия ‘#if’ или предшествующего ‘#elif’, значения которых — ложь. ‘#else’ является эквивалентом директивы ‘#elif 1’, а ‘#else’ может следовать после любого количества директив ‘#elif’, в то время как ‘#elif’ не может следовать за ‘#else’.

Если часть программы была перемещена или удалена, но есть необходимость в сохранении старого кода в качестве комментария для дальнейших ссылок к нему, то простейший способ реализации этого заключается в использовании конструкции ‘#if 0’-‘#endif’, внутри которой находится этот код. Это рациональнее применения обычных комментариев, так как это не всегда помогает, если этот код также содержит комментарии.

Такая конструкция в любом случае будет безошибочной, даже если заключенный в нее текст также содержит условия (полные конструкции ‘#if’- ‘#endif’).

Однако не следует применять такую конструкцию, если комментируемый текст не является С кодом. Для этого используются обычные С комментарии. Директива ‘#if 0’ должна состоять из правильных лексем.

Условия часто используются вместе с макросами или утверждениями, так как они являются единственными выражениями, чьи значения могут варьироваться при компиляции. Директива ‘#if’, не использующая макросы или утверждения, является эквивалентом директиве ‘#if 1’ или ‘#if 0’.

Например, рассмотрим условие, проверяющее выражение ‘BUFSIZE == 1020’, где ‘BUFSIZE’ является макросом.

#if BUFSIZE == 1020 printf ("Large buffers!\n"); #endif /* BUFSIZE is large */

При программировании часто требуется определить размер переменной или тип данных в директиве ‘#if’, но препроцессор не обрабатыват такие операторы как ‘sizeof’ или ключевые слова как ‘int’.

В директиве ‘#if’ применяется специальный оператор ‘defined’, используемый для проверки соответствия указанного имени существующему макросу. В любом случае, значением выражения ‘defined NAME’ или ‘defined (NAME)’ является 1, если в данном месте программы определен макрос с именем NAME, в противном случае значением выражения будет 0. Для оператора ‘defined’ имеет значение не определение макроса, а то что оно есть. Рассмотрим пример:

#if defined (vax) || defined (ns16000)

Здесь значением выражения будет истина, если как имя ‘vax’, так и ‘ns16000’ определены как макросы. То же самое можно выполнить с помощью утверждений:

#if #cpu (vax) || #cpu (ns16000)

Если макрос был определен, а затем уничтожен с помощью директивы ‘#undef’, то последующее применение оператора ‘defined’ возвратит значение 0, так как это имя больше не определено. Если же макрос заново определен директивой ‘#define’, то оператор ‘defined’ возвратит значение 1.

Условия, проверяющие определение одного имени довольно часто используются, поэтому для этой цели существует две дополнительные условные директивы. ‘#ifdef NAME’

что является эквивалентом ‘#if defined (NAME)’.

что является эквивалентом ‘#if ! defined (NAME)’. Макроопределения могут меняться при разных процессах компиляции по некоторым причинам.

Некоторые макросы являются заранее определенными, в зависимости от типа используемого компьютера. Например, на компьютерах Vax, имя ‘vax’ является заранее определенным макросом. На других компьютерах оно не определено.

Большое количество макросов определяется системными подключаемыми файлами. На различных системах и компьютерах определяются разные макросы с разными значениями. Очень часто полезно проверять эти макросы условными конструкциями во избежание использования аппаратных возможностей на компьютере, где они не реализованы.

Макросы являются простым способом настройки пользователями программы для различных систем или приложений. Например, макрос ‘BUFSIZE’ может быть определен в конфигурационном файле программы, который вкючается в качестве подключаемого файла в каждый исходный файл. Можно использовать макрос ‘BUFSIZE’ в условии препроцессора для генерации кода, зависящего от выбранной конфигурации.

Макросы могут определяться или уничтожаться с помощью опций препроцессора ‘-D’ и ‘-U’ при компиляции программы. Можно сделать так, что один и тот же исходный файл будет скомпилирован в две различные программы путем определения нужного макроса, использования условий для проверки значения этого макроса и передачи значения макроса через опции компилятора.

Утверждения обычно являются заранее определенными, но они также могут быть определены с помощью директив или опций препроцессора.

«Утверждения» являются более систематической альтернативой макросам при создании условий на проверку типа компьютера или системы, используемой при компиляции программы. Утверждения обычно определены заранее, хотя они могут быть также определены директивами препроцессора или с помощью опций.

Обычно макросы не классифицируются каким-либо образом по их определению. Они могут указывать на архитектуру модели компьютера, отдельную модель компьютера, на операционную систему, ее версию или на специфические возможности конфигурации. Все это может сочетаться в одном макросе. В отличие от макросов, утверждения состоят из четко поставленного вопроса и ответа на него. Вопрос обычно называется «утверждением». Утверждение выглядит следующим образом:

#PREDICATE (ANSWER)

Для имени PREDICATE следует использовать правильно сформированный идентификатор. Значением ANSWER может быть любая последоватльность слов. Здесь все символы являются значимыми, за исключением пробелов, расположенных в начале и в конце ANSWER. Различия в пробелах в середине значения игнорируются. Не разрешается использовать символ ‘)’ в значении ANSWER.

Далее приведен пример условия, проверяющего является ли ответ ANSWER утверждением PREDICATE:

#if #PREDICATE (ANSWER)

Для одного утверждения может существовать несколько ответов. Если ответ упущен при определении утверждения, то следует проверять, существует ли у данного утверждения какой-нибудь ответ:

#if #PREDICATE

Большинство проверяемых утверждений являются заранее определенными. GNU C предоставляет три заранее определенных утверждения: ‘system’, ‘cpu’ и ‘machine’. Утверждение ‘system’ используется для описания типа операционной системы, ‘cpu’ — для описания архитектуры компьютера, а ‘machine’ предоставляет дополнительную информацию о компьютере. Например, в системе GNU будут верны следующие утверждения:

#system (gnu) #system (mach) #system (mach 3) #system (mach 3.SUBVERSION) #system (hurd) #system (hurd VERSION)

а также возможно и другие. Альтернативные утверждения с более или менее подробной информацией о версии системы помогут получить ответ на вопрос о типе операционной системы.

В системе Unix существует утверждение ‘#system (unix)’, а возможно одно из следующих: `#system (aix)’, `#system (bsd)’, `#system (hpux)’, `#system (lynx)’, `#system (mach)’, `#system (posix)’, `#system (svr3)’, `#system (svr4)’, или `#system (xpg4)’ вероятно с последующей информацией о версии системы.

Другие значения для ‘system’ это ‘#system (mvs)’ и ‘#system (vms)’.

Многие Unix С компиляторы предоставляют только один ответ на утверждение ‘system’: ‘#system (unix)’, если они вообще используют утверждения.

Утверждение, ответ которого сосотоит из нескольких слов сильно отличается от утверждений с ответом из одного слова. Например, утверждение ‘system (mach 3.0)’ не означает, что ‘system (3.0)’ — верно. Это также не всегда означает, что ‘system (mach)’ тоже верно, но в GNU C последнее утверждение может быть использовано.

В настоящий момент возможные значения утверждений для ‘cpu’ являются ‘#cpu (a29k)’, `#cpu (alpha)’, `#cpu (arm)’, `#cpu (clipper)’, `#cpu (convex)’, `#cpu (elxsi)’, `#cpu (tron)’, `#cpu (h8300)’, `#cpu (i370)’, `#cpu (i386)’, `#cpu (i860)’, `#cpu (i960)’, `#cpu (m68k)’, `#cpu (m88k)’, `#cpu (mips)’, `#cpu (ns32k)’, `#cpu (hppa)’, `#cpu (pyr)’, `#cpu (ibm032)’, `#cpu (rs6000)’, `#cpu (sh)’, `#cpu (sparc)’, `#cpu (spur)’, `#cpu (tahoe)’, `#cpu (vax)’, `#cpu (we32000)’.

В С программе можно создавать свои утверждения с помощью директивы ‘#assert’ следующим образом:

#assert PREDICATE (ANSWER)

(следует заметить отсутствие симола ‘#’ перед PREDICATE.)

При каждом выполнении этой директивы создается новый правильный ответ для PREDICATE. При утверждении одного ответа предыдущие значения остаются в силе. Единственный способ удалить утверждение — использовать директиву ‘#unassert’. Эта директива имеет такой же систаксис как и ‘#assert’. Можно удалить все утверждения для PREDICATE следующим образом:

#unassert PREDICATE

Также имеется возможность добавления или удаления утверждений с помощью опций при вызове ‘gcc’ или ‘cpp’.

Директива ‘#error’ вынуждает препроцессор сделать отчет о фатальной ошибке. Все что следует после ‘#error’ используется для сообщения.

Директива ‘#error’ в теле условия, проверяющего комбинацию параметров, не до конца поддерживаемых программой, используется для сообщения о возможной ошибке. Например, если известно, что программа не совсем корректно выполняется на системе Vax, то можно написать:

#ifdef __vax__ #error Won't work on Vaxen. See comments at get_last_object. #endif

Если имеется несколько конфигурационных параметров, которые должны быть указаны соответствующим образом при установке, можно использовать условия для определения несоответствия и выдать сообщение об ошибке. Например,

#if HASH_TABLE_SIZE % 2 == 0 || HASH_TABLE_SIZE % 3 == 0 \ || HASH_TABLE_SIZE % 5 == 0 #error HASH_TABLE_SIZE should not be divisible by a small prime #endif

Директива ‘#warning’ аналогична директиве ‘#error’, но приводит к тому, что препроцессор выдает предупреждающее сообщение и продолжает обработку. Все что, что следует после ‘#warning’ используется для сообщения.

Эту директиву можно использовать в устаревших подключаемых файлах с указанием на новую версию файла.

#ifdef и #ifndef директивы (C/C++)

Директивы #ifdef и препроцессоры имеют тот же эффект, что #if и #ifndef директива, когда она используется с оператором defined .

Синтаксис

#ifdef identifier
#ifndef identifier

Эти директивы эквивалентны следующим:

#if defined identifier
#if !defined identifier

Замечания

Вы можете использовать #ifdef директивы и #ifndef директивы в любом месте #if . Оператор #ifdef identifier эквивалентен #if 1 определению identifier . Это эквивалентно #if 0 тому, когда identifier не определено или не определено директивой #undef . Эти директивы проверяют наличие или отсутствие только идентификаторов, определенных с директивой #define , а не идентификаторов, объявленных в исходном коде C или C++.

Эти директивы предназначены только для совместимости с предыдущими версиями языка. Выражение defined( identifier ) константы, используемое #if с директивой, предпочтительнее.

Директива #ifndef проверка для противоположности условия проверка. #ifdef Если идентификатор не определен или его определение было удалено с #undef , условие имеет значение true (nonzero). В противном случае условие не выполняется (false, значение равно 0).

Блок, относящийся только к системам Майкрософт

Идентификатор можно передать из командной строки с помощью /D параметра. Можно указать /D до 30 макросов.

Директива #ifdef полезна для проверка, существует ли определение, так как определение можно передать из командной строки. Например:

// ifdef_ifndef.CPP // compile with: /Dtest /c #ifndef test #define final #endif 

Завершение блока, относящегося только к системам Майкрософт

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *