
Лекция8 - Директивы препроцессора.pptx
- Количество слайдов: 26
Курс лекций по ЯП C++ и ООП на С++ Ассистент кафедры математического моделирования и информационных систем, заведующий лабораторией программно аппаратных средств защиты информации Мацула Павел Владимирович (edu. plvlml@gmail. com)
Лекция 8. ДИРЕКТИВЫ ПРЕПРОЦЕССОРА
Директивы препроцессора Препроцессором называется первая фаза компилятора. Инструкции препроцес сораназываются директивами. Они должны начинаться с символа #, перед которым в строке могут находиться только пробельные символы.
Директива #include Директива вида #include имя_файла вставляет содержимое указанного файла в ту точ куисходного файла, где она записана. Имя файла может заключаться (причем пробелы до или после имени файла недопустимы) в: угловые скобки (< >); в этом случае поиск файла, если не указан полный путь, ведется в стандартных каталогах включаемых файлов; кавычки (" "); в этом случае поиск файла ведется в каталоге, содержа щемисходный файл, а затем уже в стандартных каталогах. Директива #include является простым средством обеспечения согласованно сти объявлений в различных файлах, включая в них информацию из заголовочных файлов.
Заголовочные файлы могут содержать: определения типов, констант, встроенных функций, шаблонов, перечислений; объявления функций, переменных; пространства имен; директивы препроцессора; комментарии. В заголовочном файле не должно быть определений функций и переменных. Эти пра вилане являются требованием языка, а отражают разумный способ использова ния директивы.
Заголовочные файлы обычно имеют расширение. h или. hpp. Во многих реализациях заголовочные файлы стандартной библиотеки C++ расширения не имеют. Для каждого файла библиотеки языка С с именем
Директива #define определяет макрос – средство подстановки в тексте программы. Эта директива используется для определения: простых макросов (символических констант), все вхождения имени которых заменяются на текст подстановки: #define имя текст_подстановки сложных макросов (макросов с аргументами), которые выглядят как функции, но реализуются подстановкой их текста в текст программы: #define имя ( параметры ) текст_подстановки символов, управляющих условной компиляцией: #define имя Имена макросов рекомендуется записывать прописными буквами.
Директива #define Пример фрагмента программы: #define VERSION 4 #define BUILD 13. 666 #define VASYA "Василий Иванович" cout << BUILD << endl; cout << "Ver. VERSION. BUILD by "VASYA"n"; int num. VERSION; num 4 = 4; // ошибка
Директива #define Результат работы препроцессора: cout << 13. 666 << endl; cout << "Ver. VERSION. BUILD by ""Василий Иванович""n"; int num. VERSION; Результат выполнения программы: 13. 666 Ver. VERSION. BUILD by Василий Иванович
Директива #define Некоторые сложные макросы опасны, например: #define SQUARE(a) a*a // чем опасен? y = SQUARE(x+2); В результате работы препроцессора получится: y = x+2*x+2; // x + (2*x) + 2 Рекомендуется везде, где только можно, заключать в круглые скобки имена аргументов макросов, например: #define SQUARE(a) ((a)*(a))
Директива #define С помощью макросов можно до неузнаваемости изменить текст программы, например: #include
Директива #define Средствами макросов можно составить новую строку из двух путем использования операции препроцессора ##. Например: #define NAME 2(a, b) а##b int NAME 2(foo, bar)(); Превратится в: int foobar();
Директива #define Существует также операция препроцессора #, которая, будучи использована перед одним из параметров в теле сложного макроса, преобразует его в строковую константу. Например: #define str(x) #x cout << str(Hello. World!); Превратится в: cout << "Hello. World!";
Директива #define Благодаря использованию символа продолжения на следующую строку аналогично строковым константам, можно сымитировать поведение шаблона, например: #define CREATE_SWAP(T) void swap_##T(T&x, T&y){ T buf = x; x = y; y = buf; } CREATE_SWAP(bool) bool yes=true, no=false; … swap_bool(yes, no);
Предопределенные макросы В C++ определено несколько макросов, предназначенных в основном для того, чтобы выдавать информацию о версии программы или месте возникновения ошибки: __DATE__ – содержит строку с текущей датой в формате «Мес день год» ; __FILE__ – содержит строку с полным именем текущего файла; __LINE__ – текущая строка исходного текста; __TIME__ – текущее время в формате «чч: мм: сс» ; __cplus – определен, если используется компилятор C++; конкретный текст подстановки различается в зависимости от поддерживаемой компилятором версии стандарта.
Директива #undef Для удаления определения макроса независимо от того, существовал он до этого или нет, предусмотрена директива #undef имя Эта директива используется редко, например: для отключения какой либо опции компилятора; чтобы защититься от нежелательных макросов, так как в точности узнать их действие на фрагмент кода бывает нелегко.
Директивы условной компиляции #if, #ifdef и #ifndef применяются для того, чтобы исключить компиляцию отдельных частей программы. Это бывает полез но во многих случаях: при отладке для временного закомментирования участков кода; при поддержке нескольких версий программы для различных платформ; для того, чтобы обеспечить включение заголовочного файла только один раз.
Директивы условной компиляции Формат директивы #if: #if константное_выражение … [ #elif константное_выражение …] [ #else …] #endif
Директивы условной компиляции Количество директив #elif — произвольное. Исключаемые блоки кода могут со держатькак описания, так и исполняемые операторы. Допускается вложенность условных директив. Пример: #if VERSION == 1 #define INCFILE "ver_1. h" #elif VERSION == 2 #define INCFILE "ver_2. h" #else #define INCFILE "current. h" #endif #include INCFILE
Директивы условной компиляции В константных выражениях может использоваться проверка, определен ли макрос, с помощью логического выражения defined ( имя_макроса ) Наиболее часто в программах используются директивы #ifdef и #ifndef, позво ляющие управлять компиляцией в зависимости от того, определен ли с помощью директивы #define указанный в них макрос. Действие этих директив распространяется до первого #elif, #else или #endif.
Директивы условной компиляции Типичный пример применения – так называемая защита подключения (include guard): // Файл granny. h struct granny {}; // Файл father. h #include "granny. h" inline void father() { granny G; } // Файл mother. h #include "granny. h" inline void mother() { granny G; } // Файл child. cpp #include "father. h" #include "mother. h" father(); mother();
Директивы условной компиляции // Файл granny. h #ifndef GRANNY_H_ #define GRANNY_H_ struct granny {}; #endif // Файл father. h // Файл mother. h #include "granny. h" inline void father() inline void mother() { granny G; } // Файл child. cpp #include "father. h" #include "mother. h" father(); mother();
Директива #pragma позволяет использовать специфичные для конкретных реализаций компилятора директивы в форме #pragma имя_директивы Если используемый компилятор не поддерживает встретившуюся в тексте директиву #pragma, она игнорируется.
Директива #pragma once — нестандартная, но широко распространенная директива для контроля за тем, чтобы конкретный заголовочный файл подключался строго один раз. Она исключает риск коллизии имен макросов защиты подключения, но может привести к ошибке при работе с символьными ссылками в Unix подобной среде. Предыдущий пример можно видоизменить так: // Файл "granny. h" #pragma once struct granny {};
Директива #line позволяет переопределить имя текущего файла и номер следующей строки – например, для последующего вывода предупреждений или сообщений об ошибке, а также значений предопределенных макросов. Ее формат следующий: #line номер_строки [ "имя_файла" ] Например: #line 776 "jack. pot" cout << __FILE__ << ': '; cout << __LINE__; Результат: jack. pot: 777
Директива #error прерывает процесс компиляции, как только ее обнаруживает препроцессор, выдавая сообщение, которое можно передать ей как параметр, например: #ifndef __cplus #error A C++ compiler is required! #endif