#define DPRINT(stuff) fprintf stuff
#else
#define DPRINT(stuff)
#endif
/* В исходном файле приложения: */
DPRINT((stderr, "myvar = %d\n", myvar));
/* Обратите внимание на двойные скобки */
Обратите внимание на то, как макрос извлекается с двумя наборами скобок! Поместив весь список аргументов для fprintf()
в один аргумент, вам больше не нужно определять произвольное число отладочных макросов.
Если вы используете компилятор, удовлетворяющий стандарту С 1999 г., у вас есть дополнительный выбор, который дает наиболее чистый отладочный код:
/* МЕТОДИКА 3 --- самая чистая, но только для C99 */
/* В заголовочном файле приложения: */
#ifdef MYAPPDEBUG
#define DPRINT(mesg, ...) fprintf(stderr, mesg, __VA_ARGS__)
#else
#define DPRINT(mesg, ...)
#endif
/* В исходном файле приложения: */
DPRINT("myvar = %d\n", myvar);
DPRINT("v1 = %d, v2 = %f\n", v1, v2);
Стандарт С 1999 г. предусматривает printf()
). В макроопределении три точки '...
' означают, что будет ноль или более аргументов. В теле макроса специальный идентификатор __VA_ARGS__
замещается предусмотренными аргументами, сколько бы их ни было.
Преимуществом этого механизма является то, что при извлечении отладочного макроса необходим лишь один набор скобок, что делает чтение кода значительно более естественным. Это также сохраняет возможность использовать всего одно имя макроса вместо нескольких, которые меняются в соответствии с числом аргументов. Недостатком является то, что компиляторы C99 пока еще доступны не так широко, что снижает переносимость этой конструкции. (Однако, эта ситуация будет со временем улучшаться.)
Рекомендация: Текущие версии GCC поддерживают варьирующие макросы. Таким образом, если вы знаете, что никогда не будете использовать для компилирования своих программ что-то, кроме GCC (или какого-нибудь другого компилятора C99), можете использовать механизм C99. Однако, на момент написания, компиляторы C99 все еще не являются обычным явлением. Поэтому, если ваш код должен компилироваться разными компиляторами, следует использовать макрос в стиле с двумя парами скобок.
15.4.1.2. По возможности избегайте макросов с выражениями
В общем, макросы препроцессора С являются довольно острой палкой с двумя концами. Они предоставляют вам большую мощь, но также и большую возможность пораниться самому.[169]
Обычно для эффективности или ясности можно видеть такие макросы:
#define RS_is_null (RS_node->var_value == Nnull_string)
...
if (RS_is_null || today == TUESDAY) ...
На первый взгляд, он выглядит замечательно. Условие 'RS_is_null
' ясно и просто для понимания и абстрагирует внутренние детали проверки. Проблема возникает, когда вы пытаетесь вывести значение в GDB:
(gdb) print RS_is_null
No symbol "RS_is_null" in current context.
В таком случае нужно разыскать определение макроса и вывести развернутое значение.
Рекомендация: Для представления важных условий в своей программе используйте переменные, значения которых при изменении условий явным образом меняется в коде.
Вот сокращенный пример из io.c
в дистрибутиве gawk
:
void set_RS() {
...
RS_is_null = FALSE;
if (RS->stlen == 0) {
...
RS_is_null = TRUE;
...
matchrec = rsnullscan;
}
}
После установки и сохранения RS_is_null
ее можно протестировать в коде и вывести из-под отладчика.