_DEBUG против NDEBUG


142

Какое определение препроцессора следует использовать для указания отладочных разделов кода?

Используйте #ifdef _DEBUGили #ifndef NDEBUGили есть лучший способ сделать это, например #define MY_DEBUG?

Я думаю, что _DEBUGVisual Studio специфичен, стандарт NDEBUG?

Ответы:


113

Visual Studio определяет, _DEBUGкогда вы указываете опцию /MTdили /MDd, NDEBUGотключает утверждения стандартного Си. Используйте их по мере необходимости, т. Е. _DEBUGЕсли вы хотите, чтобы ваш код отладки соответствовал методам отладки MS CRT, и NDEBUGесли вы хотите, чтобы они были совместимы assert().

Если вы определяете свои собственные отладочные макросы (и вы не взламываете компилятор или среду выполнения C), избегайте начинать имена с подчеркивания, поскольку они зарезервированы.


19
+1. В частности, NDEBUG может быть # undef'd и # define'd в пределах одного TU (и повторное включение <assert.h> соответственно изменяет макрос assert). Поскольку это отличается от ожидаемого / желаемого, обычно используется другой макрос для управления флагом отладки "для всей компиляции", как говорится в этом ответе.

1
По-видимому, эти макросы определяют сам компилятор, а не Visual Studio (IDE). Большое спасибо за этот ответ!
Никлас Р

NDEBUG также не определяется при использовании шаблонов приложений из Windows Driver Kit 8.1. В этом случае вам может потребоваться использовать _DEBUG.
navossoc

53

Является ли стандарт NDEBUG?

Да, это стандартный макрос с семантикой «Не отлаживать» для стандартов C89, C99, C ++ 98, C ++ 2003, C ++ 2011, C ++ 2014. В стандартах нет _DEBUGмакросов.

Стандарт C ++ 2003 отправляет читателю на «странице 326» в «17.4.2.1 заголовки» к стандарту C.

Этот NDEBUG похож на Это так же, как стандартная библиотека C.

В C89 (программисты на C назвали этот стандарт стандартом C) в разделе «4.2 ДИАГНОСТИКА» было сказано

http://port70.net/~nsz/c/c89/c89-draft.html

Если NDEBUG определен как имя макроса в той точке исходного файла, в которую он включен, макрос assert определяется просто как

     #define assert(ignore) ((void)0)

Если вы посмотрите на значение _DEBUGмакросов в Visual Studio https://msdn.microsoft.com/en-us/library/b0084kay.aspx, то увидите, что этот макрос автоматически определяется вашим выбором языковой версии библиотеки времени выполнения.


50

Я полагаюсь NDEBUG, потому что это единственный, чье поведение стандартизировано по компиляторам и реализациям (см. Документацию для стандартного макроса assert). Негативная логика - это небольшая скорость чтения, но это обычная идиома, к которой можно быстро адаптироваться.

Полагаться на что-то вроде _DEBUG - значит полагаться на детали реализации конкретного компилятора и реализации библиотеки. Другие компиляторы могут или не могут выбрать то же соглашение.

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

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


1
#if !defined(NDEBUG)<- @HostileFork - это не то, что вы имели в виду? #ifнет #ifdef?
Боб Стейн

1
Оригинальный комментарий исправлен с помощью @BobStein: «Что-то, что я предпринял, чтобы (слегка) уменьшить читабельность« speedbump », это то, что когда речь идет об использовании условия без отладки #ifdef NDEBUG... но затем обратим особое внимание на отрицательную логику с помощью #if !defined(NDEBUG). В противном случае это немного трудно поймать в #ifndef NDEBUG"
HostileFork говорит, что не доверяйте SE

17

Макрос NDEBUGконтролирует, assert()являются ли операторы активными или нет.

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

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


Можете ли вы объяснить, почему вы считаете утверждения отличными от других типов отладки? В каких сценариях вы бы хотели одного, а не другого?
Эдриан Маккарти

4
Я поддерживаю утверждения активными, даже когда я не компилирую в код другие средства отладки или трассировки. Утверждения определяют невозможные ситуации. Общая трассировка и отладка IMO полностью отделены от этого.
Джонатан Леффлер

2
@AdrianMcCarthy: если у вас включена отладка ALL, это может привести к тому, что программы будут работать НЕВЕРОЯТНО медленно. Поэтому обычно классифицируют типы отладки и позволяют людям включать / отключать их отдельно. У MSVC есть два или три, которые они используют, чтобы включить / отключить различные отладки для стандартной библиотеки, такие как std :: vector.
Mooing Duck

@MooingDuck: Согласен, но различают «отладка доступна и включена» и «отладка доступна, но отключена» и «отладка недоступна». Доступное / недоступное решение - это проблема времени компиляции (в коде на C или C ++), а затем включение / отключение - это решение во время выполнения, когда доступна отладка. Только самые грубые системы отладки работают в режиме «доступные средства включены». (Тем не менее, raw может быть эффективным, и есть очень много грубых - но достаточно эффективных - отладочных систем в дикой природе!)
Джонатан Леффлер

2
@MooingDuck: Да, некоторый код отладки может изменяться медленно. В этом случае вам может понадобиться способ контролировать, выполняется ли медленный код независимо от тривиальных проверок. Но это не то же самое, что помещать утверждения в одну категорию и #ifуправляемый код в другую. assert(huge_object.IsValid());может быть медленным, а assert(ptr != nullptr);может и нет. Я согласен с Джонатаном в том, что ведение журнала и трассировка должны, вероятно, отличаться от утверждений, по крайней мере, в более крупных проектах, но я не думаю о ведении журнала или трассировке как отладочном коде, поэтому я попросил разъяснений.
Адриан Маккарти

6

Будьте последовательны, и не важно, какой. Также, если по какой-то причине вы должны взаимодействовать с другой программой или инструментом, используя определенный идентификатор DEBUG, это легко сделать.

#ifdef THEIRDEBUG
#define MYDEBUG
#endif //and vice-versa

4

К сожалению DEBUGсильно перегружен. Например, рекомендуется всегда генерировать и сохранять файл pdb для сборок RELEASE. Что означает один из -Zxфлагов и -DEBUGвариант компоновщика. Хотя _DEBUGотносится к специальным отладочным версиям библиотеки времени выполнения, таким как вызовы mallocи free. Тогда NDEBUGотключим утверждения.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.