Какова цель директивы #define в C ++? - PullRequest
6 голосов
/ 11 мая 2010

Какова роль директивы #define?

Ответы [ 8 ]

14 голосов
/ 11 мая 2010

#define используется для создания макросов в C и C ++. Подробнее об этом вы можете прочитать в документации препроцессора C . Быстрый ответ заключается в том, что он делает несколько вещей:

  1. Простые макросы - в основном просто замена текста. Постоянные времени компиляции являются хорошим примером:

    #define SOME_CONSTANT 12
    

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

    #define OPTION_1
    #define OPTION_2
    #undef  OPTION_3
    

    И тогда блоки кода в проекте будут обернуты соответствующими блоками #ifdef / #endif#, чтобы включить или отключить эти параметры в готовом проекте. Использование флага -D gcc обеспечит аналогичное поведение. Однако существуют твердые мнения о том, действительно ли этот метод является хорошим способом предоставления конфигурации для приложения.

  2. Макросы с аргументами - позволяет создавать «функциональные» макросы, которые могут принимать аргументы и манипулировать ими. Например:

    #define SQUARE(x)  ((x) * (x))
    

    вернет квадрат аргумента как его результат; будьте осторожны с потенциальным порядком операций или проблемами с побочными эффектами! Следующий пример:

    int x = SQUARE(3);     // becomes int x = ((3) * (3));
    

    будет работать нормально, но что-то вроде:

    int y = SQUARE(f());   // becomes int y = ((f()) * (f()));
    

    вызовет f() дважды или, что еще хуже:

    int z = SQUARE(x++);   // becomes int z = ((x++) * (x++));
    

    приводит к неопределенному поведению!

    В некоторых инструментах макросы с аргументами также могут быть variadic , что может пригодиться.

Как упомянуто ниже в комментариях, чрезмерное использование макросов или разработка чрезмерно сложных или запутанных макросов многими считается плохим стилем - как всегда, ставьте удобочитаемость, удобство сопровождения и отладку вашего кода выше «умных» технических приемов. .

7 голосов
/ 11 мая 2010

# define (и наоборот, #undef) может быть использовано для установки директив компилятора, которые затем можно проверить с использованием #ifndef или #ifdef Это позволяет определять пользовательское поведение в исходном файле. Обычно используется для компиляции для разных сред или для отладки кода.

Пример:

#define DEBUG



#ifdef DEBUG

//perform debug code

#endif
2 голосов
/ 11 мая 2010

Наиболее распространенное использование ( далеко ) #define для включения охранников:

// header.hh
#ifndef HEADER_HH_
#define HEADER_HH_

namespace pony {
// ...
}

#endif

Другое распространенное использование #define - это создание файла конфигурации, обычно файла config.h, где мы используем #define макросы на основе различных состояний и условий. Затем в нашем коде мы тестируем эти макросы с #ifdef, #elif defined() и т. Д., Чтобы поддерживать разные компиляции для разных ситуаций. Это не так хорошо, как идиома include-guard, и здесь нужно быть осторожным, потому что, если ветвление неправильное, вы можете получить очень неясные ошибки компилятора или, что еще хуже, поведение во время выполнения.

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

1 голос
/ 11 мая 2010

В C ++ #define выполняет очень узкие специализированные роли:

  • Заголовок, описанный в других ответах
  • Взаимодействие со стандартными библиотеками. Например, #defining WINDOWS_LEAN_AND_MEAN перед включением windows.h отключает некоторые часто проблемные макросы, такие как MAX.
  • Расширенные макросы, включающие строковое форматирование (т. Е. Макросы, которые печатают сообщения отладки) или вставку токенов.

Вам следует избегать использования #define для следующих целей. Причин много; см. instace эту запись FAQ .

  • Константы времени компиляции. Вместо этого используйте const.
  • Простые функции макросов. Вместо этого используйте inline функции и шаблоны.
1 голос
/ 11 мая 2010

Директива #define имеет два общих применения.

Первый - это контроль работы компилятора. Для этого нам также нужны #undef, #ifdef и #ifndef. (и #endif тоже ...)

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

#ifdef DEBUG

//debug code here

#endif

И вы сможете, например, скомпилировать код отладки, написав #define DEBUG

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

Пример, файл A, # включает в себя файлы B и C. Но файл B также включает в себя C. Это, вероятно, приведет к ошибке компиляции, поскольку «C» существует дважды.

Решение пишется:

#ifndef C_FILE_INCLUDED
#define C_FILE_INCLUDED

//the contents of header "c" go here.

#endif

Другое использование #define, это макросы make.

Самые простые, состоят из простых подстановок, таких как:

#define PI 3.14159265

float perimeter(float radius) {
    return radius*2*PI;
}

или

#define SHOW_ERROR_MESSAGE printf("An serious error happened");

if ( 1 != 1 ) { SHOW_ERROR_MESSAGE }

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

Но этого не следует делать по двум причинам: во-первых, макросы speed os аналогичны встроенным, а во-вторых, у нас есть шаблоны c ++, которые позволяют лучше контролировать функции с переменным типом. Таким образом, единственная причина использовать макросы с аргументами - создавать странные конструкции, которые потом будет трудно понять, например, метапрограммированные вещи ...

0 голосов
/ 11 мая 2010

Большинство вещей о #defines уже было сказано, но не ясно, что C ++ имеет лучшие замены для большинства их применений:

  1. # define для определения числовых констант может быть легко заменено константной «переменной», которая, как #define, на самом деле не существует в скомпилированном исполняемом файле. AFAIK его можно использовать практически во всех ситуациях, когда вы можете использовать #defined числовую константу, включая границы массива. Основным преимуществом для меня является то, что такие константы имеют четкую типизацию, поэтому нет необходимости добавлять приведение в макросы «просто для уверенности» и они ограничены, поэтому их можно хранить в пространствах имен / классах / функциях без загрязнения всех применение.

const int max_array_size=50;
int an_array[max_array_size];
  1. # определение для создания макросов: макросы часто можно заменить шаблонами; например, страшный макрос MAX

#define MAX(a,b)    ((a)<(b)?(b):(a))

, который имеет несколько недостатков (например, повторная оценка аргументов, неизбежное линейное расширение), может быть заменен функцией max

template<typename T> T & max(T & a, T & b)
{
    return a<b?b:a;
}

, который может быть типобезопасным (в этой версии два аргумента должны быть одного типа), может быть расширен и встроен, а не (это решение компилятора), оценивает аргументы только один раз (когда он вызывается) и ограничен. Более подробное объяснение можно найти здесь .

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

0 голосов
/ 11 мая 2010

inCorrectUseOfHashDefine ()

{

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

foreverandever

из-за:

#define foreverandever for(;;)

}

Пожалуйста, используйте константы вместо # define.

Это также для установки директив компилятора ...

0 голосов
/ 11 мая 2010

in C или C ++ # define позволяет создавать макросы препроцессора.

В обычном процессе сборки C или C ++ первое, что происходит, это то, что PreProcessor запускается, препроцессор просматривает исходные файлы для директив препроцессора, таких как # define или # include и затем выполняет простые операции с ними.

в случае директивы # define препроцессор выполняет простую подстановку на основе текста.

Например, если у вас был код

#define PI 3.14159f

float circum = diameter*PI;

препроцессор превратит его в:

float circum = diameter* 3.14159;

, просто заменив экземпляры PI на соответствующий текст. Это только самая простая форма оператора # define для более продвинутых целей, ознакомьтесь с этой статьей от MSDN

...