История развития хранилищ для нефти: Первые склады нефти появились в XVII веке. Они представляли собой землянные ямы-амбара глубиной 4…5 м...
Таксономические единицы (категории) растений: Каждая система классификации состоит из определённых соподчиненных друг другу...
Топ:
Оснащения врачебно-сестринской бригады.
Отражение на счетах бухгалтерского учета процесса приобретения: Процесс заготовления представляет систему экономических событий, включающих приобретение организацией у поставщиков сырья...
Интересное:
Распространение рака на другие отдаленные от желудка органы: Характерных симптомов рака желудка не существует. Выраженные симптомы появляются, когда опухоль...
Инженерная защита территорий, зданий и сооружений от опасных геологических процессов: Изучение оползневых явлений, оценка устойчивости склонов и проектирование противооползневых сооружений — актуальнейшие задачи, стоящие перед отечественными...
Наиболее распространенные виды рака: Раковая опухоль — это самостоятельное новообразование, которое может возникнуть и от повышенного давления...
Дисциплины:
2018-01-28 | 258 |
5.00
из
|
Заказать работу |
|
|
В пункте 1.1 было приведено определение термина "препроцессинг". В данном разделе мы рассмотрим использование директив препроцессора более подробно.
Выше мы уже сталкивались с директивой #include, она позволяет вставить содержимое указанного файла в текущее место. Имя файла может задаваться двумя способами − в угловых скобках (#include<имя_файла>) и в кавычках (#include"имя_файла"). В первом варианте поиск файла выполняется в папках, содержащих файлы стандартной библиотеки, во втором − в текущей папке и папках, указанных в командной строке компилятора.
Директива #define предназначена для определения макросов:
#define имя_макроса текст
Простейший макрос представляет собой замену имени макроса на указанный текст. Например, следующая директива:
#define EPS 1E-9
заменит в исходном файле слово EPS на текст 1E-9.
Существует набор предопределённых макросов препроцессора: например, __FILE__ заменяется на имя текущего исходного файла, __LINE__ − на номер текущей строки, __TIMESTAMP__ − на текущую дату/время и др.
С помощью директивы #define также можно создавать параметризованные макросы. Например, макрос SUM для нахождения суммы двух своих параметров выглядит так:
#define SUM(a, b) ((a) + (b))
Его использование напоминает вызов функции:
int x = SUM(2, 3);
Обратите внимание на круглые скобки вокруг параметров, а также вокруг всего выражения. Если их не написать, то результат в некоторых случаях может оказаться неверным, например:
#define SUM(a, b) a + b
int x = 5 * sum(2, 3);
В результате в переменной x окажется значение 13, а вовсе не 25, потому что выражение 5 * sum(2, 3) на стадии препроцессинга преобразуется в 5 * 2 + 3.
При написании макросов можно использовать макрооператоры # и ##. Символ #, стоящий перед названием параметра макроса, превращает значение параметра в строку (заключает его в кавычки). Например, для целей отладки может быть полезен макрос, позволяющий вывести на экран заданное выражение и его значение:
|
#define DEBUG_PRINT(x) std::cout << #x << " = " << (x) << std::endl;
int a = 3;
DEBUG_PRINT(a * 8 + 5);
В результате будет выведено a * 8 + 5 = 29
Символ ## склеивает две лексемы вместе, применяется довольно редко.
При использовании макросов стоит быть осторожным − даже правильное применение скобок не даёт полной гарантии от ошибок. Для примера рассмотрим код:
#define SQR(x) ((x) * (x))
int x = 1;
int y = SQR(x++);
Вероятно, в третьей строчке ожидалось получить результат y =1, x =2. На самом же деле получится x = 3, y = 2. Также к недостаткам макросов можно отнести то, что они затрудняют отладку программы. Мы рекомендуем не злоупотреблять макросами в своём коде. Тем не менее, в некоторых случаях макросы полезны. Например, для целей отладки может пригодиться вот такой макрос:
#define ASSERT(c) \
if (!(c)) { \
std::cerr << "\nAssertion failed in " << __FILE__ \
<< "(line " << __LINE__ << "): " << #c << std::endl; \
exit(1); \
}
Если передать ему ложное условие, например ASSERT(2 * 2!= 4), то он выведет нечто вроде " Assertion failed in test.cpp(line 12): 2 * 2!= 4" и завершит работу программы.
Следующая группа директив препроцессора − директивы условной компиляции #if, #ifdef, #ifndef, #elsif, #else, #endif. С помощью директив условной компиляции можно компилировать или пропускать компиляцию частей кода в зависимости от выполнения тех или иных условий. Типичный пример: если определён макрос DEBUG, то код для отладочной печати компилируется, в противном случае − пропускается:
#define DEBUG
#ifdef DEBUG
std::cerr << "a = " << a << std::endl;
#endif
Если закомментировать первую строчку, то отладочная печать исчезнет.
Условная компиляция широко применяется для настройки программы под конкретную платформу, а также для включения/отключения возможностей, которые будет поддерживать программа (типичный пример − сборка большинства приложений для операционных систем семейства Unix/Linux).
|
Важным частным случаем применения директив условной компиляции является предотвращение двойного подключения заголовочных файлов при компиляции. Поясним, в чём суть проблемы. Допустим, в проекте имеются файлы header1.h, header2.h и main.cpp. Пусть в файле header2.h имеется директива #include <header1.h>. В файле main.cpp стоят две директивы − #include <header1.h> и #include <header2.h>. Тогда в файл main.cpp содержимое файла header1.h вставится дважды − один раз напрямую и один раз косвенно через файл header2.h. Это, скорей всего, вызовет ошибку компиляции. Для устранения проблемы можно добавить в файл header1.h следующие строчки:
# ifndef HEADER1_H
# define HEADER1_H
...(остальной код заголовочного файла)...
# endif
Заметим, что это же самое можно сделать и проще, поместив в заголовочный файл директиву #pragma once
Директива #pragma предназначена для передачи компилятору различных инструкций. Набор допустимых инструкций зависит от конкретного компилятора. Исключением является упомянутая выше #pragma once, которая поддерживается почти всеми распространёнными компиляторами (тем не менее, и она не входит в стандарт языка).
Пример применения директивы # pragma для компилятора MS Visual C++ для установки размера стека равным 16 мегабайт:
#pragma comment(linker, "/STACK:16777216")
РАБОТА С ПАМЯТЬЮ
В данной главе рассмотрим некоторые аспекты программирования на С++, касающиеся рационального использования памяти. Оперативная память является одним из основных ресурсов компьютера, который совместно используется несколькими одновременно работающими программами, включая и операционную систему. Важно, чтобы каждая из программ использовала для обработки своих данных оптимальные структуры их хранения и не создавала проблем с памятью для себя самой (например, выход индекса за границы массива, обращение к несуществующему адресу в памяти) или для других программ (например, утечка памяти).
Для того, чтобы избежать этих и других подобных проблем, нужна не только предельная аккуратность при программировании, но и некоторые дополнительные знания, которые можно получить при чтении данной главы.
|
|
Состав сооружений: решетки и песколовки: Решетки – это первое устройство в схеме очистных сооружений. Они представляют...
Особенности сооружения опор в сложных условиях: Сооружение ВЛ в районах с суровыми климатическими и тяжелыми геологическими условиями...
Адаптации растений и животных к жизни в горах: Большое значение для жизни организмов в горах имеют степень расчленения, крутизна и экспозиционные различия склонов...
Механическое удерживание земляных масс: Механическое удерживание земляных масс на склоне обеспечивают контрфорсными сооружениями различных конструкций...
© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!