Подтвердить что ты не робот

В чем разница между макросом и константой в С++?

Мне задали этот вопрос в техническом интервью:

В чем разница между const и макросом в С++?

Мой ответ заключался в том, что макрос является директиву препроцессора, и может быть сложно отладить приложение, если вы используете макрос, поскольку он заменяется константным выражением перед компиляцией, тогда как const может иметь идентификатор типа и легко отлаживается.

Может ли кто-нибудь указать на какие-либо другие отличия и что должно быть предпочтительным?

EDIT:

Из документации IBM для С++:

Ниже перечислены некоторые различия между #define и классификатором типа const:

  • Директива #define может использоваться для создания имени для числовой, символьной или строковой константы, тогда как объект const любого типа может быть объявлен.

  • Объект const подчиняется правилам определения переменных, тогда как константа, созданная с помощью #define, не является. В отличие от объекта const значение макроса не отображается в промежуточном исходном коде, используемом компилятором, потому что они расширены внутри строки. Встроенное расширение делает макрос недоступным для отладчика.

  • Макрос может использоваться в постоянном выражении, таком как связанный массив, тогда как объект const не может. (Я думаю, что нам обязательно нужно использовать макрос для определения array_size.

  • Компилятор не проверяет тип макроса, включая аргументы макроса.

4b9b3361

Ответ 1

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

Константу, определенную с помощью классификатора const, лучше всего рассматривать как немодифицируемую переменную . Он имеет все свойства переменной: он имеет тип, имеет размер, имеет связь, вы можете взять его адрес. (Компилятор может оптимизировать некоторые из этих свойств, если это может сойти с рук: например, константы, адрес которых никогда не используется, могут не попадать в исполняемый образ, но это только по милости правила as-if. ) Единственное, что вы не можете сделать с datum const, это изменить его значение. Константа, определенная с помощью enum, немного отличается. Он имеет тип и размер, но у него нет привязки, вы не можете взять его адрес, и его тип уникален. Оба они обрабатываются во время фазы перевода 7, поэтому они не могут быть ничего, кроме lvalue или rvalue. (Я сожалею о жаргоне в предыдущем предложении, но мне пришлось бы написать несколько абзацев иначе.)

Макрос имеет гораздо меньше ограничений: он может расширяться до любой последовательности токенов, если общая программа остается хорошо сформированной программой. Он не имеет никаких свойств переменной. Применение sizeof или & к макросу может или не может сделать что-то полезное, в зависимости от того, к чему расширяется макрос. Макросы иногда определяются для расширения до числовых литералов, и такие макросы иногда считаются константами, но они не являются: "собственно компилятор" (то есть фаза перевода 7) рассматривает их как числовые литералы.

Обычно считается хорошей практикой, в настоящее время, чтобы не использовать макрос, когда будет работать константа. Макросы не подчиняются тем же правилам определения, что и все другие идентификаторы, которые могут вводить в заблуждение, и если вы используете константу, вы предоставляете больше информации для фазы перевода 7 и, следовательно, также к отладчику. Тем не менее, макросы позволяют вам делать то, что не может быть сделано каким-либо другим способом, и если вам нужно сделать одну из этих вещей, вы не должны стесняться их использовать. (Макросы, которые тянут свой вес, в этом смысле, как правило, не просто расширяются до числовых литералов, хотя я не собираюсь говорить никогда.)

EDIT: Вот пример макроса, делающего что-то интересное. Это никоим образом, не формирует и не формирует константу. Возможно, есть способ получить тот же эффект без макроса (если вы знаете тот, который не связан с строковыми потоками, мне было бы интересно узнать об этом!), Но я думаю, что это хорошая иллюстрация как власти, так и опасность макросов (для последних рассмотрим, что он будет делать, если бы использовался за пределами одного особого контекста...)

static double elapsed()
{ ... }
#define ELAPSED '[' << std::fixed << std::setprecision(2) << elapsed() << "] "

// usage:
for (vector<string>::iterator f = files.begin(); f != files.end(); f++) {
    cout << ELAPSED << "reading file: " << *f << '\n';
    process_file(*f);
}

Ответ 2

Для #define sum 1 следует выбрать const int sum = 1; по нескольким причинам:

Механизм на основе видимости:

#define не относятся к областям, поэтому нет возможности создать пространство имен с ограниченным классом. Хотя константные переменные могут быть ограничены в классах.

Избегайте странных магических чисел при ошибках компиляции:

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

Простота отладки:

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

Ответ 3

Другое отличие состоит в том, что переменная const имеет память и может ссылаться на указатель. Макро - это просто автозаполнение, которое произойдет до компиляции, поэтому имя теряется во время компиляции.

Также макрос может быть просто больше, чем константа. Это может быть выражение или что-либо синтаксически правильное, даже полное определение функции.

Макросы используются для отображения вариантов программирования, например. размер стека; а cosnt используется для отображения констант реального мира, таких как значение Pi или e.

Ответ 4

(Первоначально опубликовано для static const vs #define - воспроизводя здесь, поскольку этот вопрос, похоже, имеет больше "импульса"... дайте мне знать, если это неуместно...)

Плюсы и минусы всего, в зависимости от использования:

  • consts
    • проблемы с ограниченным охватом/идентификатором конфликта обрабатываются красиво
    • сильный, единственный, заданный пользователем тип
      • вы можете попытаться "набрать" #define ala #define S std::string("abc"), но константа избегает повторного построения отдельных временных рядов в каждой точке использования.
    • Устранение осложнений с одним определением.
    • может принимать адрес, создавать ссылки const и т.д.
  • определяет
    • "глобальная" область/более склонна к конфликтующим обычаям, что может привести к сложным задачам компиляции и неожиданным результатам во время выполнения, а не к сообщениям об ошибках; Для этого необходимо:
      • длинные, неясные и/или централизованно скоординированные идентификаторы, а доступ к ним не может получить выгоду от неявного соответствия используемого/текущего/Koenig-look-up пространства имен, псевдонимов пространства имен и т.д.
      • использование всех прописных букв обычно требуется и зарезервировано для определения препроцессора (важным ориентиром для использования препроцессора в масштабах предприятия, чтобы оставаться управляемым и какие из сторонних библиотек можно ожидать), наблюдение за которыми подразумевает миграцию существующих констант или перечисления для определения включает изменение капитализации (и, следовательно, влияет на код клиента). (Лично я использую первую букву перечислений, но не consts, поэтому меня все равно ударят - возможно, время переосмыслить это.)
    • возможны дополнительные операции компиляции: строковое литерала, конкатенация, строение (с учетом их размера)
      • Недостатком
      • является то, что при задании #define X "x" и некотором использовании клиента ala "pre" X "post" у вас возникают проблемы, если вы хотите или хотите сделать X переменную, изменяемую во время выполнения, а не константу, тогда как этот переход проще с const char* или const std::string, поскольку они уже заставляют пользователя включать операции конкатенации.
    • не может использовать sizeof непосредственно на определенной числовой константе
    • untyped (GCC не предупреждает, если сравнивать с unsigned)
    • В некоторых цепочках компилятора/компоновщика/отладчика не может присутствовать идентификатор, поэтому вы будете уменьшены до взгляда на "магические числа" (строки, что угодно...)
    • не может принимать адрес
    • замещаемое значение не обязательно должно быть законным (или дискретным) в контексте, где создается #define, поскольку оно оценивалось в каждой точке использования, поэтому вы можете ссылаться на еще не объявленные объекты, зависят от "реализации", которые не нужно предварительно включать, создавать "константы", такие как { 1, 2 }, которые могут использоваться для инициализации массивов или #define MICROSECONDS *1E-6 и т.д. (определенно не рекомендуя это!)
    • некоторые специальные вещи, такие как __FILE__ и __LINE__, могут быть включены в макроподстановку
  • перечислений
    • возможно только для целых значений
    • проблемы с ограниченным охватом/идентификатором конфликта обрабатываются красиво
    • строго типизирован, но к достаточно большому размеру signed-or-unsigned int, над которым у вас нет контроля (в С++ 03)
    • не может принимать адрес
    • более сильные ограничения использования (например, incrementing - template <typename T> void f(T t) { cout << ++t; } не будет компилироваться)
    • каждый тип константы, взятый из охватывающего перечисления, поэтому template <typename T> void f(T) получает отдельное представление, когда передается одно и то же числовое значение из разных перечислений, все из которых отличаются от любого фактического экземпляра f (int).
    • даже с typeof, не может ожидать, что numeric_limits будут полезны для понимания.
    • имя enum может отображаться в разных местах RTTI, сообщения компилятора и т.д. - возможно, полезно, возможно обфускация

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

Ответ 5

Макросы не относятся к области видимости, а имя макроса может быть недоступно для символического отладчика. Дэн Сакс содержит довольно полную статью об относительных достоинствах макросов (none), константных объектов и констант перечисления. Как Stephen Dewhurst, Сакс предпочитает константы перечисления для целочисленных значений, так как они не занимают никакого хранилища (точнее, константы перечисления не имеют ни продолжительности хранения, ни привязки).

Ответ 6

define может быть переопределен, но const будет причиной ошибки компилятора:

Пример: источник: main.cpp

#define int_constance 4
#define int_constance 8 // ok, compiler will warning ( redefine macro)

const int a = 2;
const int a = 4; // redefine -> error

int main(int argc, char** argv)
{
   std::cout << int_constance ; // if remove second #define line, output will be 8

   return 0;
}

Ответ 7

Макрос всегда имеет тип, например, #define FIVE 5 имеет тип int.

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