#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
Завершение блока, относящегося только к системам Майкрософт
C define как в c
Директива #define определяет идентификатор и последовательность символов, которые будут подставляться вместо идентификатора каждый раз, когда он встретится в исходном файле. Формальное определение директивы:
#define идентификатор последовательность_символов
Используем директиву #define:
#include #define N 23 int main(void) < int x = N; printf("Number: %d", x); // Number: 23 return 0; >
Здесь определен один идентификатор N. В программе, где встречается этот идентификатор, он будет заменяться на число 23. Например, строка
int x = N;
после обработки препроцессором будет иметь следующий код
int x =23;
Более сложный пример
#include #define BEGIN < #define END >#define N 23 int main(void) BEGIN int x = N; printf("Number: %d", x); // Number: 23 return 0; END
Здесь определены три идентификатора BEGIN, END, N. В итоге все вхождения последовательности символов «BEGIN» будут заменяться на открывающую фигурную скобку, а «END» — на закрывающую, а символ «N» на число 23.
Таким образом, после обработки препроцессора функция main приобретет следующий вид:
int main(void)
#define также может определять более сложные выражения. Например:
#include #define ADD(a,b) (a+b) int main(void) < int n1 = 10; int n2 = 5; printf("%d + %d = %d", n1, n2, ADD(n1, n2)); // 10 + 5 = 15 >
В данном случае выражение ADD(a,b) будет заменяться операцией сложения двух чисел (a + b)
Особенно удобно использовать директиву #define для определения размеров массивов:
#include #define N 4 int main(void) < int numbers[N] = ; for(int i=0; i return 0; >
В данном случае если мы захотим глобально поменять размер массива, то достаточно изменить значение N в директиве define.
Следует учитывать, что директива препроцессор не заменяет последовательности символов в двойных и одинарных кавычках и в комментариях:
#include #define N 4 int main(void) < char symbol = 'N'; printf("%c \n", symbol); // N printf("N"); //N return 0; >
Причем если идентификатор должен представлять одно слово, то его последовательность символов может состоять из нескольких слов или символов, разделенных пробелами:
#define REAL long double
Директива #undef
В процессе работы мы можем многократно определять новое значение для одного идентификатора:
#define N 23 #define N 32 #define N 55
Но некоторые компиляторы, в частности, gcc, могут выдавать предупреждения при повторном определении идентификатора, и чтобы выйти из этой ситуации, мы можем использовать директиву #undef для отмены действия макроса. Эта директива имеет следующее определение:
#undef идентификатор
#include #define STRING «Good morning \n» int main(void)
#define CPP WTF

Уже давным-давно я работал в одной крупной компании в должности C++-разработчика и столкнулся с одной очень странной ошибкой. Я написал примерно такой класс:
class Foo < static void* operator new() < return . ; >; >;
И увидел огромный stack-trace ошибок о недопустимом вызове оператора в этом контекста (на тот момент я использовал MS Visual Studio 2013 и встроенный в него MSVC-компилятор). Я искал проблему часа два, и помогло мне только просматривание готовой единицы трансляции. Как вы могли догадаться, проблема была связана с препроцессором, но обо всём по порядку.
Препроцессор в C++ — это такая, на первый взгляд, очень простая штука, основное назначение которой — добавлять в ваш исходный файл куски кода до того, как им займётся компилятор (для знающих: препроцессор формирует единицы трансляции). Он подключает include-файлы, обрабатывает всякие там #pragma once , но самая главная директива препроцессора, безусловно, это директива #define . Она позволяет заменять один кусок текста на другой. Например, вы пишете #define foo goo , и после этого на этапе работы препроцессора все упоминания отдельного токена (слова) foo в вашем коде заменяются на goo .
Это очень мощный инструмент, он поддерживает аргументы и даже раскрывает последовательности (правда, без рекурсии):
#define foo goo #define goo doo foo(); // тут вызовется doo
Единственная проблема — препроцессор очень тупой. Он совершенно не следит за тем, что именно вы define’ите. Например, можно сделать так: #define float double , и это действительно заменит все float на double ; или так: #define std qwerty , и это заменит все упоминания пространства имён std на qwerty ( std::cout -> qwerty::cout ).
Препроцессор действует на уровне единицы трансляции, то есть #define , объявленный в файле a.cpp, не будет действовать в файле b.cpp. Но там, где он действует, он будет действовать беспощадно 🙂 #define , которого вы не ожидаете может очень сильно изменить логику работы программы и неприятно удивить. Ровно так и получилось в моём случае: в одном из include-файлов было такое объявление:
#define new DEBUG_NEW
а в другом include-файле, такое:
#define DEBUG_NEW new(__LINE__, __FILE__)
Что сделал наш волшебный препроцессор? Правильно, честно всё заменил, и в результате получилось вот так:
class Foo < static void* operator new(__LINE__, __FILE__)() < return . ; >; >;
Для чего нужен DEBUG_NEW — вопрос отдельный. Коротко говоря, это отладочный оператор выделения памяти, ведущий учёт всех запрошенных блоков. Тем не менее, теперь ошибка синтаксиса языка уже становится очевидной. Однако это не отменяет того факта, что искать её было очень тяжело.
Успешной вам отладки 🙂
Как работает #define, какой тип данных хранит или как его определяет?
Какой тип данных хранит #define или как оно его определяет? Просто в printf в данном случае понятно, что сначала выводится целое число, а потом число флоатное, которое является результатом уравнения. Мне не понятно каким образом оно может посчитать данное уравнение, если тип данных переменной farh, которая в нём находится, по моему, не определён? И как всё это связано с #define ? У меня есть предположение по этому поводу, но они не очень ясные и вообще хотелось услышать конкретное пояснение.
Отслеживать
13.7k 12 12 золотых знаков 43 43 серебряных знака 75 75 бронзовых знаков
задан 23 авг 2021 в 18:25
AlinaHeldman AlinaHeldman
81 6 6 бронзовых знаков
Работа препроцессора в этой книжке описывается в главе 4.
24 авг 2021 в 10:10
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
#define хранит не какие-то типизированные данные, а просто пару «имя-значение». Перед компиляцией все «имена» заменяются на «значения».
Дальше всё делает компилятор так, как будто никаких дефайнов и не было, а с самого начала текст был написан с использованием значений, определенных в #define.
В общем, #define — это тупая подстановка определенного в #define значения вместо определенного в #define же имени по всему коду. Отсюда следует, что вообще в #define можно запихнуть любую дичь, и пример #define true false — это еще довольно невинный пример.
Также, важно понимать, что подстановкой дефайнов занимается не компилятор, а препроцессор. И у него интеллекта при этом не больше, чем у функции replace(). То есть он тупо заменяет всё по тексту.
Использовать это можно, к примеру, так:
#define myint unsigned long long; myint a = 1099511627776;
ведь правда, писать myint вместо unsigned long long — короче?
А еще в комментариях любезно подсказывают: «Чтобы понять зачем #define нужен, посмотрите сначала про #ifdef. Эта команда очень нужна для компилирования на разные архитектуры процессоров/операционных систем». Вот это — настоящее, а не учебное использование дефайнов.