C++ - enum против const против #define

В конце статьи здесь: http://www.learncpp.com/cpp-tutorial/45-enumerated-types/ упоминает следующее:

Наконец, как и в случае с постоянными переменными, перечисляемые типы отображаются в отладчике, что делает их более полезными, чем значения #defined.

Как достигается смелое предложение выше?

Спасибо.


person Simplicity    schedule 22.01.2011    source источник
comment
Я отредактировал заголовок и добавил один тег, надеюсь, вы не будете против. :-)   -  person Nawaz    schedule 22.01.2011
comment
@Наваз. Это совершенно нормально, не беспокойтесь об этом.   -  person Simplicity    schedule 22.01.2011
comment
возможный дубликат static const vs #define в c   -  person Matthieu M.    schedule 10.09.2011
comment
@MatthieuM. Конечно нет. Правила C++ настолько отличаются, что заслуживают другого ответа.   -  person Pharap    schedule 10.08.2019


Ответы (7)


Рассмотрим этот код,

#define WIDTH 300

enum econst
{
   eWidth=300
};

const int Width=300;

struct sample{};

int main() 
{
        sample s;
        int x = eWidth * s; //error 1
        int y = WIDTH * s;  //error 2
        int z = Width * s;  //error 3
        return 0;
}

Очевидно, что каждое умножение приводит к ошибке компиляции, но посмотрите, как GCC генерирует сообщения для каждой ошибки умножения:

prog.cpp:19: ошибка: нет совпадения для 'operator*' в 'eWidth * s'
prog.cpp:20: ошибка: нет совпадения для 'operator*' в '300 * s'
prog. cpp: 21: ошибка: не соответствует «оператор *» в «Ширина * s»

В сообщении об ошибке вы не видите макрос WIDTH, который у вас есть #defined, верно? Это связано с тем, что к моменту, когда GCC пытается скомпилировать строку, соответствующую второй ошибке, он не видит WIDTH, всего видит только 300, так как до того, как GCC скомпилирует строку, препроцессор уже заменил WIDTH с 300. С другой стороны, ничего подобного не происходит с enum eWidth и const Width.

Посмотрите на ошибку здесь: http://www.ideone.com/naZ3P


Также прочтите Item 2 : Prefer consts, enums, and inlines to #defines из Effective C++ Скотта Мейерса.

person Nawaz    schedule 22.01.2011
comment
По общему признанию, Clang, вероятно, выдаст лучшее сообщение об ошибке, показав строку, вызвавшую ошибку, и, таким образом, включив макрос (вместе с его расширением). Хотя я согласен с тем, что макросы не следует использовать для констант, я думаю, что лучший подход к этому вопросу не будет использовать сообщения об ошибках gcc в качестве краеугольного камня рассуждений. - person Matthieu M.; 10.09.2011
comment
@MatthieuM.: Clang может выдавать лучшее сообщение об ошибке, но те, кто использует GCC для компиляции своего кода, не собираются перекомпилировать свой код с помощью Clang, чтобы увидеть лучшее сообщение об ошибке. {продолжение} - person Nawaz; 27.03.2012
comment
{contd}... Таким образом, такое рассуждение очень сильно зависит от компилятора, и поэтому я думаю, что использование сообщения об ошибке Clang в качестве краеугольного камня рассуждений также не будет лучше, потому что кто-то может прийти и сказать в точности то, что вы сказали, заменив < i>Clang с GCC, MSVC и лучше с не так лучше, заканчивая свой комментарий на I думаю, что лучший подход к вопросу не будет использовать сообщения об ошибках Clang в качестве краеугольного камня рассуждений . Надеюсь, вы поняли мою точку зрения. :-) - person Nawaz; 27.03.2012
comment
Я понимаю, что вы пытаетесь получить, но я не согласен. Я думаю, что использование сообщений об ошибках компилятора в качестве аргумента для перечислений, а не макросов, слабо, в том смысле, что это зависит от того, как компилятор создает эти сообщения об ошибках, которые меняются от компилятора к компилятору и от версии к версии. . Но тогда сам вопрос довольно странный, поскольку он основан на нынешнем поведении отладчиков... так что мне все равно. - person Matthieu M.; 27.03.2012
comment
@MatthieuM.: Я не вижу другого лучшего аргумента в пользу предпочтения const вместо #define, если этот аргумент слаб. Насколько хорош (и практичен) аргумент, можно сказать в свете всех других аргументов. Поведение компиляторов в зависимости от языка определяет, что следует использовать. Иначе что еще решает? - person Nawaz; 27.03.2012
comment
вы имеете в виду, помимо того факта, что макросы не имеют области действия и нетипизированы, очевидно? - person Matthieu M.; 27.03.2012
comment
Я знаю, что этот вопрос довольно старый, как и этот ответ, но у меня всегда был один вопрос по этому поводу. Использование памяти. Что из трех приведет к созданию переменной, которая займет место в скомпилированной программе? - person RedX; 07.02.2014
comment
@RedX: макрос (например, #define X 40) не занимает памяти. Но... пожалуйста, не беспокойтесь об этом. Даже если константные переменные занимают несколько байтов, используйте переменные const. Во многих случаях компилятор может оптимизировать код, поэтому даже константные переменные могут сэкономить вам память (если они действительно константы). Или используйте enum, они могут сэкономить вам байты. - person Nawaz; 07.02.2014
comment
@Nawaz Я разрабатываю код для встроенных систем, поэтому для меня естественно беспокоиться о количестве глобальных переменных и занимаемом ими месте. Я довольно долго искал способ сократить использование #define, но всегда беспокоился об использовании памяти. - person RedX; 07.02.2014
comment
@RedX: В этом случае enum должен помочь вам в C (или в C++ вы также можете использовать постоянное выражение, определенное с помощью const или constexpr). Попробуйте их. - person Nawaz; 07.02.2014
comment
Ссылка, упомянутая в посте, похоже, никуда не ведет - person RBT; 26.08.2016
comment
Ссылка не работает Решение не найдено - person Pharap; 10.08.2019
comment
Также это уже не так, современная версия GCC дает: <source>:16:19: error: no match for 'operator*' (operand types are 'int' and 'sample') 16 | int y = WIDTH * s; //error 2 - person Pharap; 10.08.2019

enum — это постоянная времени компиляции с отладочной информацией без выделения памяти.

const выделяется хранилищем, в зависимости от того, оптимизировано ли оно компилятором с постоянным распространением.

#define не имеет места для хранения.

person Sun Shine    schedule 02.09.2013
comment
enum будет занимать место для хранения всякий раз, когда в вашей программе объявляется переменная этого типа enum. Не так ли? Я полагаю, что ваше упоминание о enum относится к случаю, когда переменная этого типа enum никогда не объявляется в программе. Но зачем кому-то объявлять перечисление и не использовать его? - person RBT; 26.08.2016
comment
Я бы +1, если бы вы могли уточнить тот факт, что отсутствие выделения памяти не означает, что память не используется. Все это требует, чтобы данные где-то хранились, но разница между выделением памяти и ее отсутствием заключается в том, где именно. - person Pharap; 10.08.2019

#define значения заменяются препроцессором на значение, в котором они объявлены, поэтому в отладчике он видит только значение, а не имя #defined, например если у вас #define NUMBER_OF_CATS 10, в отладчике вы увидите только 10 (поскольку препроцессор заменил каждый экземпляр NUMBER_OF_CATS в вашем коде на 10.

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

person Jackson Pope    schedule 22.01.2011

Компилятор сохраняет информацию о перечислении в двоичном файле, когда программа компилируется с определенными параметрами.

Когда переменная имеет тип перечисления, отладчик может показать имя перечисления. Лучше всего это показать на примере:

enum E {
    ONE_E = 1,
};

int main(void)
{
    enum E e = 1;

    return 0;
}

Если вы скомпилируете это с помощью gcc -g, вы можете попробовать следующее в gdb:

Reading symbols from test...done.
(gdb) b main
Breakpoint 1 at 0x804839a: file test.c, line 8.
(gdb) run
Starting program: test 

Breakpoint 1, main () at test.c:7
7               enum E e = 1;
(gdb) next
9               return 0;
(gdb) print e
$1 = ONE_E
(gdb) 

Если бы вы использовали определение, у вас не было бы подходящего типа для предоставления e, и вам пришлось бы использовать целое число. В этом случае компилятор напечатает 1 вместо ONE_E.

Флаг -g просит gdb добавить отладочную информацию в двоичный файл. Вы даже можете увидеть, что он есть, выполнив:

xxd test | grep ONE_E

Однако я не думаю, что это будет работать во всех архитектурах.

person Penz    schedule 22.01.2011
comment
Спасибо за ответ. Что вы подразумеваете под: компилятор хранит информацию перечисления в двоичном файле? Спасибо. - person Simplicity; 22.01.2011
comment
Буквально двоичный файл содержит эту информацию в формате, который понимает gdb. Из руководства gcc: -g Выдавать отладочную информацию в собственном формате операционной системы (...). GDB может работать с этой отладочной информацией. - person Penz; 22.01.2011

По крайней мере, для Visual Studio 2008, который сейчас у меня под рукой, это предложение верно. Если у вас есть

#define X 3
enum MyEnum
{
    MyX = 3
};

int main(int argc, char* argv[])
{
    int i = X;
    int j = (int)MyX;
    return 0;
}

и вы устанавливаете точку останова в main, вы можете навести указатель мыши на «MyX» и увидеть, что он оценивается как 3. Вы не увидите ничего полезного, если наведете курсор на X.

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

person Philipp    schedule 22.01.2011
comment
Это является свойством языка, поскольку X даже не отображается компилятором (только предварительно обработанное значение 3), поэтому у отладчика нет абсолютно никакой возможности использовать X в качестве символического имени, поэтому он покажет вы только простое значение 3. Это верно для всех компиляторов и IDE. OTOH, показывающий такие символы, как MyX в отладчике, действительно является особенностью IDE, но она настолько распространена, что я сомневаюсь, что есть какая-либо современная IDE, которая не предлагает этого. - person Péter Török; 22.01.2011
comment
@PéterTörök Ничто не мешает кому-то создать компилятор, который также выполняет предварительную обработку или создает способ размещения X в таблице символов. - person Pharap; 10.08.2019

Я отвечаю слишком поздно, но чувствую, что могу что-то добавить — enum, const и #define.

перечисление

  1. Не требует присвоения значений (если вы просто хотите иметь последовательные значения 0, 1, 2..), тогда как в случае #define вам нужно вручную управлять значениями, которые иногда могут вызвать человеческую ошибку
  2. Он работает так же, как переменная, чтобы во время онлайн-отладки значение enum можно было наблюдать в окне просмотра.
  3. У вас может быть переменная типа перечисления, которой вы можете назначить перечисление

    номера перечисления typedef { DFAULT, CASE_TRUE, CASE_OTHER, };

    int main(void) {число чисел = CASE_TRUE; }

константа

  1. It is constant stored in read only area of memory but can be accessed using address which is not possible in case of #define
    1. You have type check in your hand if you use const rather than #define
  2. определения являются директивой предварительной обработки, но const - это время компиляции, например

    const char *name = "викас";

Вы можете получить доступ к имени и использовать его базовый адрес для чтения, например, vikas[3] для чтения «a» и т. д.

#define — это тупые директивы препроцессора, которые выполняют замену текста.

person Community    schedule 30.07.2013

Проверьте следующую статью, хорошее резюме http://www.queryhome.com/26340/define-vs-enum-vs-constant

person Community    schedule 29.12.2013