Мало того, что необходимо поместить parens вокруг аргументов, необходимо поместить parens вокруг возвращенного выражения.
#define MIN(a,b) a < b ? a : b // WRONG
int i = MIN(1,2); // works
int i = MIN(1,1+1); // breaks
#define MIN(a,b) (a) < (b) ? (a) : (b) // STILL WRONG
int i = MIN(1,2); // works
int i = MIN(1,1+1); // now works
int i = MIN(1,2) + 1; // breaks
#define MIN(a,b) ((a) < (b) ? (a) : (b)) // GOOD
int i = MIN(1,2); // works
int i = MIN(1,1+1); // now works
int i = MIN(1,2) + 1; // works
Однако MIN(3,i++)
все еще повреждается...
лучшее правило только для использования #defines только, когда НИКАКОЙ ДРУГОЙ ПОДХОД не будет РАБОТАТЬ! я знаю, что Вы спрашиваете о C вместо C++, но все еще переносите его в памяти.
Используйте круглую скобку вокруг всего макроса и вокруг каждого аргумента, упомянутого в списке расширения:
#define MAX(x, y) ((x) > (y) ? (x) : (y))
<час> Стараются не писать макросы, которые оценивают их аргументы многократно. Такие макросы не будут вести себя как ожидалось, когда аргументы будут иметь побочные эффекты:
MAX(a++, b);
оценит a++
дважды, если a
будет больше, чем b
.
ВЕРХНИЙ РЕГИСТР Использования называет для макросов, чтобы прояснить, что это - макрос и не функция так, чтобы различия можно было рассмотреть соответственно (другая общая хорошая практика не передает аргументы, которые имеют побочные эффекты к функциям любой).
<час>не используют макросы для переименования типов как это:
#define pint int *
, потому что это не будет вести себя как ожидалось, когда кто-то введет
pint a, b;
определения типов Использования вместо этого.
При выполнении макроса, который должен выполнить его аргумент и вести себя как выражение, это идиоматично:
#define DOIT(x) do { x } while(0)
Эта форма имеет следующие преимущества:
используйте статические значения константы вместо макросов для постоянных величин, интеграла или другого. Компилятор может часто оптимизировать их далеко, и они остаются 1-м гражданином класса в системе типов языка.
static const int DANGER = 60 + 2;
В расширении, помещенной круглой скобке вокруг аргументов, так, чтобы, если они передают в выражении, Вы получили намеченное поведение.
#define LESS_THAN(X,Y) (((X) < (Y) ? (X) : (Y))
Ответ на макросы МАКСА/МИН, взятые от GCC, взламывает в ядре Linux :
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
Используйте довольно уникальные имена для своих макросов, так как они имеют глобальную область видимости и могут столкнуться с чем-либо, таким образом:
#define MAX 10
мог легко столкнуться с другим кодом, таким образом:
#define MYPROJECT_MAX 10
или что-то еще более уникальное, было бы лучше.
я видел случаи, где столкновение этого вида не произвело ошибку компиляции, но генерировало немного неправильный код, таким образом, это может быть довольно коварно.
Не определите свои макросы.
Ваш #defines
должен соответствовавший #undef
. Это препятствует препроцессору то, чтобы быть забитым и влияние на непреднамеренные части кода.
Если Вы осторожны и опытны, можно быть в состоянии выполнить DRY (Don't-Repeat-Yourself) код, при помощи макросов как простые генераторы кода. Действительно необходимо объяснить другим программистам, что Вы делаете, но это может сохранить много кода. Например, макро-списком техника:
// define a list of variables, error messages, opcodes
// or anything that you have to write multiple things about
#define VARLIST \
DEFVAR(int, A, 1) \
DEFVAR(double, B, 2) \
DEFVAR(int, C, 3) \
// declare the variables
#define DEFVAR(typ, name, val) typ name = (val);
VARLIST
#undef DEFVAR
// write a routine to set a variable by name
void SetVar(string varname, double value){
if (0);
#define DEFVAR(typ, name, val) else if (varname == #name) name = value;
VARLIST
#undef DEFVAR
else printf("unrecognized variable %s\n", varname);
}
// write a routine to get a variable's value, given its name
// .. you do it ..
Теперь, если Вы хотите добавить новую переменную, удаляют один или переименовывают один, это - редактирование с 1 строкой.
Посмотрите, как я ненавижу это:
void bar(void) {
if(some_cond) {
#define BAZ ...
/* some code */
#undef BAZ
}
}
Всегда помещает их как это:
void bar(void) {
if(some_cond) {
#define BAZ ...
/* some code */
#undef BAZ
}
}