2011-01-22 1 views
27

В конце статьи здесь: http://www.learncpp.com/cpp-tutorial/45-enumerated-types/, он упоминает следующее:C++ - перечисление против сопзЬ против #define

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

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

Спасибо.

+0

Я редактировал название и добавил один тег, надеюсь, вы не возражаете. :-) – Nawaz

+0

@Nawaz. Все в порядке, не беспокойтесь об этом. – Simplicity

+0

Возможный дубликат ["static const" vs "#define" в c] (http://stackoverflow.com/questions/1674032/static-const-vs-define-in-c) –

ответ

28

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

#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: error: no match for ‘operator*’ in ‘eWidth * s’
prog.cpp:20: error: no match for ‘operator*’ in ‘300 * s’
prog.cpp:21: error: no match for ‘operator*’ in ‘Width * s’

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

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


Кроме того, прочитайте Item 2 : Prefer consts, enums, and inlines to #defines из Эффективное C++ Скотт Мейерс.

+1

Понятно, что Clang, вероятно, создаст лучшее сообщение об ошибке, показывая линию, вызвавшую ошибку, и, таким образом, включит макрос (вместе с его расширением). Хотя я согласен, что макросы не должны использоваться для констант, я думаю, что лучшее обращение к вопросу не будет использовать сообщения об ошибках gcc как краеугольный камень рассуждений. –

+0

@MatthieuM .: Clang может дать лучшее сообщение об ошибке, но те, кто использует GCC для компиляции своего кода, не собираются перекомпилировать свой код с Clang, чтобы увидеть лучшее сообщение об ошибке. {contd} – Nawaz

+0

{contd} ... Так что такое рассуждение очень зависит от компилятора, и поэтому я думаю, что принятие сообщения об ошибке Clang в качестве краеугольного камня рассуждений тоже не будет лучше, потому что кто-то может прийти и сказать точно, что вы сказал, заменив * «Clang» * на * «GCC, MSVC» * и * «лучше» * с * «не очень-то лучше» *, закончив свой компъюент с * «Я думаю, что лучшее обращение с вопросом не будет используйте ** сообщения об ошибках Clang ** как краеугольный камень рассуждений *. Надеюсь, ты понял. :-) – Nawaz

0

По крайней мере, для 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; 
} 

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

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

+2

Это ** ** языковое свойство, в том, что 'X' даже не рассматривается компилятором (только предварительно обработанное значение' 3'), поэтому отладчик не имеет абсолютно никакого способа использовать 'X' в качестве символического имени, поэтому он покажет вам только обычную значение '3'. Это справедливо для всех компиляторов и IDE. OTOH, показывающий такие символы, как 'MyX' в отладчике, действительно является особенностью, специфичной для IDE, но это так распространено. Я сомневаюсь, что существует какая-то современная среда разработки, которая этого не предлагает. –

6

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

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

3

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

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

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 

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

+0

Спасибо за ваш ответ. Что вы подразумеваете под: «Компилятор хранит информацию об перечислении в двоичном формате»? Благодарю. – Simplicity

+0

Буквально бинарный файл имеет эту информацию в формате, который gdb понимает. Из руководства gcc: -g Производить отладочную информацию в собственном формате операционной системы (...).GDB может работать с этой отладочной информацией. – Penz

0

я отвечаю слишком поздний, но я чувствую, что я могу добавить что-то - перечислимой против сопзИ против #define

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

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

    ЬурейиХ перечислений номера { DFAULT, CASE_TRUE, CASE_OTHER, };

    int main (void) { number number = CASE_TRUE; }

Const -

  1. Он постоянен хранится в области только для чтения памяти, но можно получить доступ, используя адрес, который не является возможным в случае #define
    1. У вас есть тип проверки в руке, если вы используете const, а не #define
  2. определяет директиву предварительной обработки, но const - время компиляции например

    const char * name = "vikas";

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

#defines - немые директивы препроцессора, который делает текстуальные замена

10

enum - это постоянная времени компиляции с информацией об отладке без распределения хранилища.

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

#define не имеет ассигнований на хранение.

+2

Лучший краткий ответ. – tprk77

+0

'enum' будет принимать пространство для хранения всякий раз, когда переменная этого типа enum объявляется в вашей программе. Не так ли? Я полагаю, что ваше упоминание о 'enum' относится к случаю, когда переменная этого типа enum никогда не объявляется в программе. Но зачем кому-то объявлять перечисление и не использовать его? – RBT