Директивы препроцессора. Препроцессорные директивы

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

В 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 , которая служит для подключения заголовочных файлов.

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

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

Начнем с директивы #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++ применяется следующий формат

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

Директива #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!" ;

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

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

Определение

Назначение

Определение макроса

Отмена определения макроса

Включение объекта-заголовка

Компиляция, если выражение истинно

Компиляция, если макрос определен

Компиляция, если макрос не определен

Компиляция, если выражение в ifложно

Составная директива else/if

Окончание группы компиляции по условию

Замена новым именем строки или имени исходного файла

Формирование ошибок трансляции

Действие определяется реализацией

Null- директива

Директива # define

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

# define ИМЯ_МАКРОСА последовательность_символов

Последовательность символов называют еще строкой замещения. Когда препроцессор находит в исходном файле имя_макроса (просто макрос), он заменяет его на последовательность_символов.

Можно отменить определение макроса директивой # undef:

# undef имя_макроса

Данная строка удаляет любую ранее введенную строку замещения. Определение макроса теряется и имя_макроса становится неопределенным.

К примеру, можно определить МАХ как величину 100:

Это значение будет подставляться каждый раз вместо макроса МАХ в исходном файле, Можно также использовать макрос вместо строковой константы:

#defineNAME“TurboC++”

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

Директива # define имеет еще одну важную особенность: макрос может иметь аргументы. Каждый раз, когда происходит замена, аргументы заменяются на те, которые встречаются в программе.

Пример : #define MIN(a, b) ((9a)<(b)) ? (a) : (b)

printf(“Минимум из x и y “ % d, MIN(x ,y));

printf(“Минимум из a и b “ % d, MIN(n ,m));

Когда программа будет компилироваться, в выражение, определенное MIN(a,b) будут подставлены соответственноxиyилиmиn. Аргументыaиbзаключены в круглые скобки, так как вместо них может подставляться некоторое выражение, а не просто идентификатор.

Например, printf(“Минимум “ %d,MIN(x*x,x));

Директива # error

Имеет вид: # error сообщение_об_ошибке

Эта команда прекращает компиляцию программы и выдает сообщение об ошибке.

Директивы условной компиляции

К данным директивам относятся: # if , # else , # elif , # endif .

Данные директивы производят выборочную компиляцию программы. Если выражение, следующее за #if, истинно, то коды, заключенные между #ifи #endif, будут компилироваться. В противном случае они при компиляции будут пропущены. Выражение, следующее за #if, проверяется во время компиляции, поэтому оно может содержать только константы и макросы, которые прежде определены. Переменные здесь не могут использоваться.

Директива # else используется так же, как иelseв языке Си.

Пример: Использование условной компиляции.

# include

# define MAX 100

printf(“ MAX равно %d \n”, MAX);

Директива # elif используется для организации вложенной условной компиляции. Форма использования ее следующая:

#if<выражение>

последовательность операторов

#elif<выражение 1>

последовательность операторов

#elif<выражение 2>

последовательность операторов

…………………………………..

Другой метод условной компиляции состоит в использовании директив # ifdef и# ifndef . Основная форма использования этих директив следующая:

# ifdef ИМЯ_МАКРОСА

# endif

и соответственно

# ifndef ИМЯ_МАКРОСА

последовательность операторов

# endif

Если макрос определен, то при использовании # ifdefкомпилируется соответствующая последовательность до операторов #endif. Если же макрос не определен или был отменен директивой #undef, то соответствующая последовательность операторов игнорируется компилятором. Директива #ifndefдействует противоположным образом.

#include

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

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

Если подключаемый файл не найден, процесс компиляции завершается с ошибкой.

Директива #define

Директива #define принимает две формы:

  • определение констант;
  • определение макросов.
Определение констант
#define nameToken value

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

#include #define TEXT "Марс" // определение константы int main() { std::cout << TEXT; return 0; }

Как видите, для доступа к значению константы, просто используем её имя.

Определение параметризованных макросов

#define nameMacros(arg1, arg2, ...) expression

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

#define MAX(num1, num2) ((num1) > (num2) ? (num1) : (num2))

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

Директива #undef

Директива #undef переопределяет константу или препроцессорный макрос, ранее определенный с помощью директивы #define .

#undef nameToken

Давайте посмотрим пример использования директивы #undef:

#define E 2.71828 // раннее определенный макрос int sumE = E + E; // обращение к макросу #undef E // теперь E - не макрос

Как правило, директива #undef используются для снятия, ранее определенной константы или макроса, в небольшой области программы. Это делается для того, чтобы для всей программы, макроc или константа оставались, а для некоторой области, эти же макрос или константа могут быть переопределены. Небезопасно было бы во всей программе переопределять константу, но в короткий области, это сравнительно безопасно. Директива #undef является единственным способом создания этой области, так как область действия макросов или констант действует от директивы #define до #undef .

Директива #if

#if value // код, который выполнится, в случае, если value - истина #elsif value1 // этот код выполнится, в случае, если value1 - истина #else // код, который выполнится в противном случае #endif

Директива #if проверяет, является ли значение value истиной и, если это так, то выполняется код, который стоит до закрывающей директивы #endif . В противном случае, код внутри #if не будет компилироваться, он будет удален компилятором, но это не влияет на исходный код в исходнике.

Обратите внимание, что в #if могут быть вложенные директивы #elsif и #else . Ниже показан пример кода для комментирования блоков кода, используя следующую конструкцию:

#if 0 // код, который необходимо закомментировать #endif

Если у вас в программе есть блоки кода, которые содержат многострочные комментарии и вам требуется обернуть полностью этот блок кода в комментарий — ничего не получится, если вы воспользуетесь /*многострочный комментарий*/ . Другое дело — конструкция директив #if #endif .

Директива #ifdef

#ifdef nameToken // код, который выполнится, если nameToken определен #else // код, который выполнится, если nameToken не определен #endif

Директива #ifdef проверяет, был ли ранее определен макрос или символическая константа как #define . Если — да, компилятор включает в программу код, который находится между директивами #ifdef и #else , если nameToken ранее определен не был, то выполняется код между #else и #endif , или, если нет директивы #else , компилятор сразу переходит к #endif . Например, макрос __cpp определен в C++, но не в Си. Вы можете использовать этот факт для смешивания C и C++ кода, используя директиву #ifdef:

#ifdef __cpp // C++ код #else // Си код #endif

Директива #ifndef

#ifndef nameToken // код, который выполнится, если nameToken не определен #else // код, который выполнится, если nameToken определен #endif

Директива #ifndef проверяет, был ли ранее определен макрос или символическая константа как #define . Если — да, компилятор включает в программу код, который находится между директивами #else и #endif , если nameToken ранее определен не был, то выполняется код между #ifndef и #else , или, если нет директивы #else , компилятор сразу переходит к #endif . Директива #ifndef может быть использована для подключения заголовочных файлов. если они не подключены, для этого использовать символическую константу, как индикатор подключенного к проекту функционала.

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

#ifndef PRODUCT_H #define PRODUCT_H class Product { // код класса... }; #endif PRODUCT_H

В этом случае используется пустая символьная константа PRODUCT_H , которая может быть определена в программе только вместе с классом Product . Поэтому, если мы обнаружим, что константа PRODUCT_H уже определена, значит класс тоже и тогда мы исключим повторное определение класса, которое может привести к ошибке переопределения.

Директива #error

#error "Этот код не должен компилироваться"

Директива #error позволяет отображать в списке ошибок компиляции сообщение, в случае возникновения соответствующей ошибки. Эту директиву наиболее полезно использовать в сочетании с директивами #if , #elsif , #else для проверки компиляции, если некоторое условие не верно. Например:

#ifndef __unix__ // __unix__ обычно поддерживается в юникс-системах #error "Поддерживается только в Unix" #endif

Препроцессорный макрос __FILE__

Препроцессорный макрос __FILE__ расширяется до полного пути к текущему файлу (исходнику). __FILE__ полезен при создании лог-файла, генерации сообщений об ошибках, предназначенных для программистов, при отладки кода.

Int error (const char* adrFile, const std::string& erMessage) { cerr << "[" << adrFile << "]" << arMessage << endl; } #define LOG(erMessage) error(__FILE__, arMessage) // макрос LOG может быть использован для получения сообщений об ошибках, которые выводятся на стандартный поток ошибок

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

Препроцессорный макрос __LINE__

Макрос __LINE__ разворачивается в текущий номер строки в исходном файле, как целое значение. __LINE__ полезен при создании лог-файла или генерации сообщений об ошибках с указанием номера строки, предназначенных для программистов, при отладки кода.

Int error (int nLine, const std::string& erMessage) { cerr << "[" << nLine << "]" << erMessage << endl; } #define LOG(erMessage) error(__LINE__, erMessage) // макрос LOG может быть использован для получения сообщений об ошибках, с указанием номеров строк, которые выводятся на стандартный поток ошибок

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

Препроцессорный макрос __DATE__

Макрос __DATE__ раскрывается в текущую дату (время компиляции) в виде [ммм дд гггг] (например, «Dec 7 2012″), как строка. __DATE__ может быть использован для предоставления информации о времени компиляции.

Cout << __DATE__ << endl;

Вы можете также использовать макрос __TIME__ , чтобы получить текущее время компиляции.

Препроцессорный макрос __TIME__

Макрос __TIME__ раскрывается в текущее время (время компиляции) в формате чч: мм:cc в 24-часовом формате (например, «22:29:12″). Макрос __TIME__ может быть использован для предоставления информации о времени в конкретный момент компиляции.

Cout << __TIME__ << endl;

Препроцессорный макрос __TIMESTAMP__

Макрос __TIMESTAMP__ раскрывается в текущее время (время компиляции) в формате Ddd Mmm Date hh::mm::ss yyyy, время в 24-часовом формате:

  • Ddd это сокращенно день недели,
  • ммм это сокращенно месяц,
  • Date — текущий день месяца (1-31),
  • гггг — это четыре цифры года.

Например, "Fri Dec 7 00:42:53 2012" . Макрос __TIMESTAMP__ может быть использован для получения информации о дате и времени компиляции.

Cout << __TIMESTAMP__ << endl;

Вы можете также использовать макрос __TIME__ , чтобы получить текущее время компиляции и макрос __DATE__ для получения даты.

Директива #pragma

#pragma compiler specific extension

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

#pragma once // заголовочный файл

В этом примере, директива #pragma once не позволяет включать файл в проект несколько раз, то есть предотвращает переопределение.

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

#pragma warning (disable: 4018)

Директива #pragma в этом примере используется для отключения предупреждений 4018. Для получения дополнительной использования директивы #pragma , обратитесь к документации вашего компилятора.

Макро оператор #

#

Оператор # текстовую лексему в строку, заключенную в кавычку. Смотрим пример:

#include using namespace std; #define message(s) cout << "Сообщение: " #s << endl; int main() { message("GunGame"); return 0; }

Выполняется конкатенация строк и макрос message разворачивается в cout << "Сообщение: GunGamen"; . Обратите внимание на то, что операция # должна использоваться совместно с аргументами, так как # ссылается на аргумент.

Макро оператор ##

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

#define type ch##ar type a; // переменная a тип данных char, так как ch и ar склеились в char

Рассмотрим еще один пример использования оператора ## , в котором объединим две лексемы:

#define TOKENCONCAT(x,y) x##y

Когда в программе выполняется вызов этого макроса, две лексемы объединяются в одну. Операция ## обязательно должна иметь два операнда.

P.S.: Любая серьёзная программа должна иметь свою базу данных, обычно для управления БД используются следующие СУБД: MySQL, MsSQL, PostgreeSQL, Oracle и др. В установке sql server, форум на cyberforum.ru будет для вас не заменимым помощником. Задавайте свои вопросы на этом форуме, вам обязательно помогут в решении вашей проблемы.