Директивы компилятора c. Директивы препроцессора в си. Что будет, если на компьютере нет STL

Возможности 20.03.2019
Возможности

Директивы препроцессора

В C# определен ряд директив препроцессора, оказывающих влияние на интерпретацию исходного кода программы компилятором. Эти директивы определяют порядок интерпретации текста программы перед ее трансляцией в объектный код в том исходном файле, где они появляются. Термин директива препроцессора появился в связи с тем, что подобные инструкции по традиции обрабатывались на отдельной стадии компиляции, называемой препроцессором. Обрабатывать директивы на отдельной стадии препроцессора в современных компиляторах уже не нужно, но само ее название закрепилось.

Все директивы препроцессора начинаются со знака #. Кроме того, каждая директива препроцессора должна быть выделена в отдельную строку кода. Принимая во внимание современную объектно-ориентированную архитектуру языка C#, потребность в директивах препроцессора в нем не столь велика, как в языках программирования предыдущих поколений. Тем не менее они могут быть иногда полезными, особенно для условной компиляции. В этой статье все директивы препроцессора рассматриваются по очереди.

Директива #define

Директива #define определяет последовательность символов, называемую идентификатором. Присутствие или отсутствие идентификатора может быть определено с помощью директивы #if или #elif и поэтому используется для управления процессом компиляции. Ниже приведена общая форма директивы #define:

#define идентификатор

Обратите внимание на отсутствие точки с запятой в конце этого оператора. Между директивой #define и идентификатором может быть любое количество пробелов, но после самого идентификатора должен следовать только символ новой строки. Так, для определения идентификатора EXPERIMENTAL служит следующая директива:

#define EXPERIMENTAL

В C/C++ директива #define может использоваться для подстановки исходного текста, например для определения имени значения, а также для создания макрокоманд, похожих на функции. А в C# такое применение директивы #define не поддерживается. В этом языке директива #define служит только для определения идентификатора.

Директивы #if и #endif

Обе директивы, #if и #endif, допускают условную компиляцию последовательности кода в зависимости от истинного результата вычисления выражения, включающего в себя один или несколько идентификаторов. Идентификатор считается истинным, если он определен, а иначе - ложным. Так, если идентификатор определен директивой #define, то он будет оценен как истинный. Ниже приведена общая форма директивы #if:

#if идентификаторное_выражение последовательность операторов #endif

Если идентификаторное_выражение, следующее после директивы #if, истинно, то компилируется код (последовательность операторов), указываемый между ним и директивой #endif. В противном случае этот промежуточный код пропускается. Директива #endif обозначает конец блока директивы #if. Идентификаторное выражение может быть простым, как наименование идентификатора. В то же время в нем разрешается применение следующих операторов: !, ==, !=, && и ||, а также круглых скобок.

Ниже приведен пример применения упомянутых выше директив:

// Объявляем идентефикаторное выражение #define EXP #define TR using System; namespace ConsoleApplication1 { class Program { static void Main() { #if EXP Console.WriteLine("Проверка идентификатора EXP"); #endif #if EXP && TR Console.WriteLine("Проверка идентификатора EXP и TR"); #endif #if RES Console.WriteLine("Данный код не выполнится, т.к. такой идентификатор отсутствует"); #endif Console.ReadLine(); } } }

Директивы #else и #elif

Директива #else действует аналогично условному оператору else языка C#, определяя альтернативный ход выполнения программы, если этого не может сделать директива #if.

Обозначение #elif означает "иначе если", а сама директива #elif определяет последовательность условных операций if-else-if для многовариантной компиляции. После директивы #elif указывается идентификаторное выражение. Если это выражение истинно, то компилируется следующий далее кодовый блок, а остальные выражения директивы #elif не проверяются. В противном случае проверяется следующий по порядку блок. Если же ни одну из директив #elif не удается выполнить, то при наличии директивы #else выполняется последовательность кода, связанная с этой директивой, а иначе не компилируется ни один из кодовых блоков директивы #if.

Ниже приведена общая форма директивы #elif:

#if идентификаторное_выражение последовательность операторов #elif идентификаторное_выражение последовательность операторов #elif идентификаторное_выражение последовательность операторов //... #endif

Давайте добавим в предыдущий пример следующий код:

#if EXP Console.WriteLine("Проверка идентификатора EXP"); #endif #if EXP && TR Console.WriteLine("Проверка идентификатора EXP и TR"); #else Console.WriteLine("Данный код не выполнится"); #endif

Директива #undef

С помощью директивы #undef удаляется определенный ранее идентификатор. Это, по существу, означает, что он становится "неопределенным". Ниже приведена общая форма директивы #undef:

#undef идентификатор

Рассмотрим следующий пример кода

// Объявляем идентефикаторное выражение #define ID //удаляем идентификатор #undef ID using System; namespace ConsoleApplication1 { class Program { static void Main() { #if ID Console.WriteLine("Данное выражение не выполнится"); #endif Console.ReadLine(); } } }

Директива #undef применяется главным образом для локализации идентификаторов только в тех фрагментах кода, в которых они действительно требуются.

Директива #error

Директива #error вынуждает компилятор прервать компиляцию. Она служит в основном для отладки. Ниже приведена общая форма директивы #error:

#error сообщение_об_ошибке

Когда в коде встречается директива #error, выводится сообщение об ошибке. Например, когда компилятору встречается строка кода « #error Это тестовая ошибка! » компиляция прерывается и выводится сообщение "Это тестовая ошибка!".

Директива #warning

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

#warning предупреждающее_сообщение

Директива #line

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

#line номер "имя_файла"

Имеются еще два варианта директивы #line. В первом из них она указывается с ключевым словом default, обозначающим возврат нумерации строк в исходное состояние, как в приведенном ниже примере:

#line default

А во втором варианте директива #line указывается с ключевым словом hidden. При пошаговой отладке программы строки кода, находящиеся между директивой #line hidden и следующей директивой #line без ключевого слова hidden, пропускаются отладчиком.

Препроцессор - это специальная программа, являющаяся частью компилятора языка Си. Она предназначена для предварительной обработки текста программы. Препроцессор позволяет включать в текст программы файлы и вводить макроопределения.
Работа препроцессора осуществляется с помощью специальных директив (указаний). Они отмечаются знаком решетка #. По окончании строк, обозначающих директивы в языке Си, точку с запятой можно не ставить.

Основные директивы препроцессора

#include - вставляет текст из указанного файла
#define - задаёт макроопределение (макрос) или символическую константу
#undef - отменяет предыдущее определение
#if - осуществляет условную компиляцию при истинности константного выражения
#ifdef - осуществляет условную компиляцию при определённости символической константы
#ifndef - осуществляет условную компиляцию при неопределённости символической константы
#else - ветка условной компиляции при ложности выражения
#elif - ветка условной компиляции, образуемая слиянием else и if
#endif - конец ветки условной компиляции
#line - препроцессор изменяет номер текущей строки и имя компилируемого файла
#error - выдача диагностического сообщения
#pragma - действие, зависящее от конкретной реализации компилятора.

Директива #include

Директива #include позволяет включать в текст программы указанный файл. Если файл является стандартной библиотекой и находится в папке компилятора, он заключается в угловые скобки <> .
Если файл находится в текущем каталоге проекта, он указывается в кавычках "" . Для файла, находящегося в другом каталоге необходимо в кавычках указать полный путь.

#include
#include "func.c"

Директива #define

Директива #define позволяет вводить в текст программы константы и макроопределения.
Общая форма записи

#define Идентификатор Замена


Поля Идентификатор и Замена разделяются одним или несколькими пробелами.
Директива #define указывает компилятору, что нужно подставить строку, определенную аргументом Замена , вместо каждого аргумента Идентификатор в исходном файле. Идентификатор не заменяется, если он находится в комментарии, в строке или как часть более длинного идентификатора.

1
2
3
4
5
6
7
8

#include
#define A 3
int main()
{
printf("%d + %d = %d" , A, A, A+A); // 3 + 3 = 6
getchar();
return 0;
}

В зависимости от значения константы компилятор присваивает ей тот или иной тип. С помощью суффиксов можно переопределить тип константы:

  • U или u представляет целую константу в беззнаковой форме (unsigned );
  • F (или f ) позволяет описать вещественную константу типа float ;
  • L (или l ) позволяет выделить целой константе 8 байт (long int );
  • L (или l ) позволяет описать вещественную константу типа long double

#define A 280U // unsigned int
#define B 280LU // unsigned long int
#define C 280 // int (long int)
#define D 280L // long int
#define K 28.0 // double
#define L 28.0F // float
#define M 28.0L // long double

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

идентификатор(аргумент1, ..., агрументn)


замещается версией аргумента замена , в которой вместо формальных аргументов подставлены фактические аргументы.

Пример на Си : Вычисление синуса угла

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#include
#include
#include
#define PI 3.14159265
#define SIN(x) sin(PI*x/180)
int main()
{
int c;
system("chcp 1251" );
system("cls" );
printf("Введите угол в градусах: " );
scanf("%d" , &c);
printf("sin(%d)=%lf" , c, SIN(c));
getchar(); getchar();
return 0;
}

Результат выполнения

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

Однако при использовании таких макроопределений следует соблюдать осторожность, например

1
2
3
4
5
6
7
8
9
10
11
12
13

#include
#define sum(A,B) A+B
int main()
{
int a, b, c, d;
a = 3; b = 5;


getchar();
return 0;
}


Результат выполнения:


По умолчанию текст макроопределения должен размещаться на одной строке. Если требуется перенести текст макроопределения на новую строку, то в конце текущей строки ставится символ "обратный слеш" — \ .

1
2
3
4
5
6
7
8
9
10
11
12
13
14

#include
#define sum(A,B) A + \
B
int main()
{
int a, b, c, d;
a = 3; b = 5;
c = (a + b) * 2; // c = (a + b)*2
d = sum(a, b) * 2; // d = a + b*2;
printf(" a = %d\n b = %d\n" , a, b);
printf(" c = %d \n d = %d \n" , c, d);
getchar();
return 0;
}


Кроме того, директива #define позволяет замещать часть идентификатора. Для указания замещаемой части используется ## .

1
2
3
4
5
6
7
8
9

#include
#define SUM(x,y) (a##x + a##y)
int main()
{
int a1 = 5, a2 = 3;
printf("%d" , SUM(1, 2)); // (a1 + a2)
getchar();
return 0;
}


Результат выполнения:

Директивы #if или #ifdef/#ifndef вместе с директивами #elif , #else и #endif управляют компиляцией частей исходного файла.
Если указанное выражение после #if имеет ненулевое значение, в записи преобразования сохраняется группа строк, следующая сразу за директивой #if . Синтаксис условной директивы следующий:

1
2
3
4
5
6
7

#if константное выражение
группа операций
#elif константное выражение
группа операций
#else
группа операций
#endif


Отличие директив #ifdef/#ifndef заключается в том, что константное выражение может быть задано только с помощью #define .

У каждой директивы #if в исходном файле должна быть соответствующая закрывающая директива #endif . Между директивами #if и #endif может располагаться любое количество директив #elif , однако допускается не более одной директивы #else . Директива #else , если присутствует, должна быть последней перед директивой #endif .

Пример

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#include
#include
#define P 2
int main()
{
system("chcp 1251" );
system("cls" );
#if P==1
printf("Выполняется ветка 1" );
#elif P==2
printf("Выполняется ветка 2, P=%d" , P);
#else
printf("Выполняется другая ветка, P=%d" , P);
#endif
getchar();
return 0;
}

В этой статье мы продолжим постигать искусство программирования на языке С++. На этом этапе обучения пора познакомится с такими вещами, как директивы препроцессора. Забегая наперед скажу, что в предыдущих уроках мы уже использовали директиву #include , которая служит для подключения заголовочных файлов.

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

Как видно перед самой компиляцией исходный текст программы обрабатывает препроцессор, давайте познакомимся с его инструкциями поближе.

Начнем с директивы #include, которая заменяется препроцессором на содержимое следующего за ней файла. Пример использования #include:

#include

#include «header2.h»

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

#ifndef CUCUMBLER_H

#define CUCUMBLER_H

/* содержимое файла cucumbler.h */

Директива #ifndef выполняет проверку не была ли определена константа CUCUMBLER_H ранее, и если ответ отрицательный, то выполняется определение данной константы, и прочего кода, который следует до директивы #endif. Как не сложно догадаться директива #define определяет константу CUCUMBLER_H. В данном случае подобный кусок кода помогает избежать многократного включения одного и того же кода, так как после первого включения проинициализируется константа CUCUMBLER_H и последующие проверки #ifndef CUCUMBLER_H будут возвращать FALSE.

Директива #define широко применяется и при отладке программы.

#include

#include

#include

using namespace std;

cout << "Начало функции main()\n";

vector text_array;

while (cin >> text)

cout << "Прочитан текст: " << text << "\n";

text_array.push_back(text);

Если константа IN_DEBUG не задана, то препроцессор сгенерирует следующий исходник:

#include

#include

#include

using namespace std;

vector text_array;

while (cin >> text)

text_array.push_back(text);

Но если определить IN_DEBUG, то текст программы кардинальным образом поменяется

#include

#include

#include

using namespace std;

cout << "Начало функции main()\n";

vector text_array;

while (cin >> text)

cout << "Прочитан текст: " << text << "\n";

text_array.push_back(text);

Задать препроцессорную константу можно прямо из консоли. Например для компилятора g++ применяется следующий формат

Одной из самых распространенных ошибок программистов считают создания велосипедов, колеса, и т.д. С этим невероятно сложно не согласиться, потому что на практике так и есть, но как с этим бороться? Многие программисты скажут вам в один голос: учить STL - стандартную библиотеку, которая содержит наработки множества разработчиков языка программирования, а также неплохо может помочь при разработке новой программы. В данной статье мы подробно разберем, что же такое C #Include, как это работает и для чего создавалось. Статья рекомендуется не только начинающим пользователям операционной системы Windows, но и программистам первого-второго курса.

Что это такое?

Для начала стоит просветить тех, кто сталкивается с программированием впервые и не знает, тем более, про тонкости сборки программ. Изначально люди программировали на машинных языках, после научно-технического прогресса в области микропроцессоров было принято решение создать операционную систему для больших масс пользователей.

Задумка была хорошей, но очень сложной в воплощении, поскольку на это бы ушли десятилетия, если использовать для построения программ. Тогда же было поручено еще одно задание: создать язык программирования, на котором легко будет создать операционную систему, таким языком стал С.

Да, многие скажут, что там нет как на С++. Это так, но можно написать ОС и на функциях, чему доказательством являются Linux и ядро Windows. В данном языке программирования каждая библиотека подключается в начале файла, все благодаря метки препроцессора С #include. После ключевого слова с решеткой должно быть название файла следующим образом: <так, если файл в папке с компилятором> и “так, если он вне папки компилятора”. Пример правильного подключения: #include

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

Библиотеки Visual С Include

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

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

Одним из лучших IDE по праву считают Microsoft Visual Studio. Более того, в данной среде разработки даже немного видоизменился сам язык, поэтому в данном пункте мы разберем самые востребованные стандартные библиотеки Visual C.

Список библиотек:

  1. Time.h - заголовочный файл для работы с временными интервалами.
  2. Stdlib.h - заголовочный файл с подключенным классом стандартной библиотеки.
  3. Stdio.h - стандартная библиотека ввода\ввыода.
  4. Fsteam.h - библиотека для работы с файлами.

Также имеются другие, намного более специфические библиотеки, но их изучение рекомендуется исключительно тогда, когда нужно работать только с возможностями Visual Studio. К слову, существует несколько вариаций данного IDE, от чего и зависят наборы стандартных библиотек, например, в версии PRO есть инструменты для работы с Android, а в обычной такого нет.

Список всех Include с пояснениями на С

Вы знаете, что такое классы? Если нет, то у вас появились вопросы, почему же библиотеки заканчиваются на «.h». Если быть кратким, то классы - это определенного рода кубики «Лего», которые можно вставить в программу. Чтоб это было легко - они и существуют. По правилу хорошего тона программирования, объявления их параметров надо заполнять в заголовочном файле, а само выполнение в отдельном с расширением «*. с» или «*. cpp».

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

Список STL:

  1. Vector.h - работа с динамической памятью, называемой векторами.
  2. Map.h - специальные словари.
  3. Iostream - библиотека для работы с вводом и выводом в консоль.
  4. Fout - работа с файлами. Аналогом является C Include .
  5. Stdlib - является классом наследником от других STL.
  6. Errno.h - заголовочный файл для выведения ошибок в консоль.
  7. Ctype.h - заголовочный файл для работы с аски-кодом.

Стандартная библиотека - это невероятное сборище различных творений создателя языка, а также множества других программистов. Использование STL поощряется на любом уровне. Также имеется и немало других, менее известных библиотек, например, С Include - это библиотека для работы с сигналами системы, но их изучение требуется в узкоспециализированных программах.

В С++

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

Плюсом является высокая скорость полученной программы, т.е. ее быстродействие, поэтому данный язык используют практически везде, где только можно, даже в бытовых приборах есть обязательная строчка кода на С++.

Что будет, если на компьютере нет STL?

В С и С++ существует динамическая и статическая компиляция. В зависимости от системы, под которую изготовляется продукт, также и вкладываются соответствующие библиотеки. Например, библиотеки Windows.h нет на Linux, а на Windows нет x11.lib (там у библиотек такое расширение - *.lib). Данный факт требуется учитывать, но благодаря умным IDE можно выбрать между динамической и статической сборкой. При динамической - от системы специфические библиотеки, а в статической проект занимает больше, но гарантированно идет в конкретной системе.

В заключение

Надеемся, что вам понравилось окунуться в столь удивительный и красочный мир программирования. Если вы никогда не писали собственную программу, то рекомендуем начать это делать прямо сейчас, ведь вкус победы над машиной слишком приятен. Также надеемся, что изучение c Include вам далось без особых осложнений. В любом случае, можете навестить MDSN и узнать еще больше.

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

Директива #include

Вы уже видели директиву #include в действии. Когда вы #include файл, препроцессор копирует содержимое подключаемого файла в текущий файл сразу после строчки #include. Это очень полезно при использовании определённых данных (например, функций) сразу в нескольких местах.

Директива #include имеет две формы:

#include , которая сообщает препроцессору искать файл в системных путях. Чаще всего вы будете использовать эту форму при подключении из стандартных библиотек C++.

#include "filename" , которая сообщает препроцессору искать файл в текущей директории проекта. Если его там не окажется, то препроцессор начнёт проверять системные пути и любые другие, которые вы указали в настройках вашей . Эта форма используется для подключения пользовательских заголовочных файлов.

Директива #define

Директиву #define можно использовать для создания макросов. Макрос — это правило, которое определяет конвертацию идентификатора в указанные данные.

Есть два основных типа макросов: макросы-функции и макросы-объекты.

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

Макросы-объекты можно определить одним из двух следующих способов:

#define identifier
#define identifier substitution_text

Верхнее определение не имеет никакого substitution_text , в то время как нижнее — имеет. Поскольку это директивы препроцессора (а не простые ), то ни одна из форм не заканчивается точкой с запятой.

Макросы-объекты с substitution_text

Когда препроцессор встречает макросы-объекты с substitution_text , то любое дальнейшее появление identifier заменяется на substitution_text . Идентификатор обычно пишется заглавными буквами с символами подчёркивания вместо пробелов.

Рассмотрим следующий фрагмент кода:

#define MY_FAVORITE_NUMBER 9 std::cout << "My favorite number is: " << MY_FAVORITE_NUMBER << std::endl;

#define MY_FAVORITE_NUMBER 9

std :: cout << "My favorite number is: " << MY_FAVORITE_NUMBER << std :: endl ;

Препроцессор преобразует код выше в:

std::cout << "My favorite number is: " << 9 << std::endl;

Любое дальнейшее появление идентификатора USE_YEN удаляется и заменяется «ничем» (пустым местом)!

Это может показаться довольно бесполезным, однако, это не основное предназначение подобных директив. В отличие от макросов-объектов с substitution_text , эта форма макросов считается приемлемой для использования.

Условная компиляция

Директивы препроцессора условной компиляции позволяют определить, при каких условиях код будет компилироваться, а при каких — нет. В этом уроке мы рассмотрим только три директивы условной компиляции:

#ifdef;

#ifndef;

#endif.

Директива #ifdef (англ. «if def ined» = «если определено») позволяет препроцессору проверить, было ли значение ранее #define. Если да, то код между #ifdef и #endif скомпилируется. Если нет, то код будет проигнорирован. Например:

#define PRINT_JOE #ifdef PRINT_JOE std::cout << "Joe" << std::endl; #endif #ifdef PRINT_BOB std::cout << "Bob" << std::endl; #endif

#define PRINT_JOE

#ifdef PRINT_JOE

std :: cout << "Joe" << std :: endl ;

#endif

#ifdef PRINT_BOB

std :: cout << "Bob" << std :: endl ;

#endif

Поскольку PRINT_JOE уже был #define, то строчка std::cout << "Joe" << std::endl; скомпилируется и выполнится. А поскольку PRINT_BOB не был #define, то строчка std::cout << "Bob" << std::endl; не скомпилируется и, следовательно, не выполнится.

Директива #ifndef (англ. «if n ot def ined» = «если не определено») - это полная противоположность #ifdef, которая позволяет проверить, не было ли значение ранее определено. Например:

#ifndef PRINT_BOB std::cout << "Bob" << std::endl; #endif

#ifndef PRINT_BOB

std :: cout << "Bob" << std :: endl ;

#endif

Результатом выполнения этого фрагмента кода будет Bob , так как PRINT_BOB ранее никогда не был #define. Условная компиляция очень часто используется в качестве header guards (о них мы поговорим в следующем уроке).

Область видимости директивы #define

Директивы выполняются перед компиляцией программы: сверху вниз, файл за файлом.

Рассмотрим следующую программу:

#include void boo() { #define MY_NAME "Alex" } int main() { std::cout << "My name is: " << MY_NAME; return 0; }

#include

void boo ()

#define MY_NAME "Alex"

int main ()

std :: cout << "My name is: " << MY_NAME ;

return 0 ;

Несмотря на то, что директива #define MY_NAME "Alex" определена внутри функции boo, препроцессор этого не заметит, так как он не понимает такие понятия C++, как функции. Следовательно, выполнение этой программы будет идентично той, в которой бы #define MY_NAME "Alex" было определено ДО, либо сразу ПОСЛЕ функции boo. Для лучше читабельности кода определяйте идентификаторы (с помощью #define) вне функций.

После того, как препроцессор завершит своё выполнение, все идентификаторы (определённые с помощью #define) из этого файла отбрасываются. Это означает, что директивы действительны только от точки определения до конца файла, в котором они определены. Директивы, определённые в одном файле кода, не влияют на директивы, определённые внутри других файлов этого же проекта.

Рассмотрим следующий пример:

#include void doSomething() { #ifdef PRINT std::cout << "Printing!"; #endif #ifndef PRINT std::cout << "Not printing!"; #endif }

#include

void doSomething ()

#ifdef PRINT

std :: cout << "Printing!" ;

#endif

#ifndef PRINT

std :: cout << "Not printing!" ;



Рекомендуем почитать

Наверх