Хорошие практики программирования для макроопределений (#define) в [закрытом] C

24
задан a3f 31 March 2015 в 01:53
поделиться

10 ответов

Мало того, что необходимо поместить 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++, но все еще переносите его в памяти.

29
ответ дан Roddy 28 November 2019 в 22:14
поделиться

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

#define MAX(x, y) ((x) > (y) ? (x) : (y))
<час>

Стараются не писать макросы, которые оценивают их аргументы многократно. Такие макросы не будут вести себя как ожидалось, когда аргументы будут иметь побочные эффекты:

MAX(a++, b);

оценит a++ дважды, если a будет больше, чем b.

<час>

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

<час>

не используют макросы для переименования типов как это:

#define pint int *

, потому что это не будет вести себя как ожидалось, когда кто-то введет

pint a, b;

определения типов Использования вместо этого.

10
ответ дан Robert Gamble 28 November 2019 в 22:14
поделиться

При выполнении макроса, который должен выполнить его аргумент и вести себя как выражение, это идиоматично:

 #define DOIT(x) do { x } while(0)

Эта форма имеет следующие преимущества:

  1. Этому нужна завершающаяся точка с запятой
  2. , Это работает с вложением и фигурными скобками, например, с если/еще
29
ответ дан unwind 28 November 2019 в 22:14
поделиться

используйте статические значения константы вместо макросов для постоянных величин, интеграла или другого. Компилятор может часто оптимизировать их далеко, и они остаются 1-м гражданином класса в системе типов языка.

static const int DANGER = 60 + 2;
6
ответ дан John Dibling 28 November 2019 в 22:14
поделиться

В расширении, помещенной круглой скобке вокруг аргументов, так, чтобы, если они передают в выражении, Вы получили намеченное поведение.

#define LESS_THAN(X,Y) (((X) < (Y) ? (X) : (Y))
5
ответ дан EvilTeach 28 November 2019 в 22:14
поделиться

Ответ на макросы МАКСА/МИН, взятые от GCC, взламывает в ядре Linux :

#define min(x, y) ({                       \
        typeof(x) _min1 = (x);             \
        typeof(y) _min2 = (y);             \
        (void) (&_min1 == &_min2);         \
        _min1 < _min2 ? _min1 : _min2; })
5
ответ дан dalle 28 November 2019 в 22:14
поделиться

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

#define MAX 10

мог легко столкнуться с другим кодом, таким образом:

#define MYPROJECT_MAX 10

или что-то еще более уникальное, было бы лучше.

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

2
ответ дан DarthPingu 28 November 2019 в 22:14
поделиться

Не определите свои макросы.

Ваш #defines должен соответствовавший #undef. Это препятствует препроцессору то, чтобы быть забитым и влияние на непреднамеренные части кода.

2
ответ дан Drew Noakes 28 November 2019 в 22:14
поделиться

Если Вы осторожны и опытны, можно быть в состоянии выполнить 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 строкой.

1
ответ дан Mike Dunlavey 28 November 2019 в 22:14
поделиться

Посмотрите, как я ненавижу это:

void bar(void) {
    if(some_cond) {
        #define BAZ ...
        /* some code */
        #undef BAZ
    }
}

Всегда помещает их как это:

void bar(void) {
    if(some_cond) {
#define BAZ ...
        /* some code */
#undef BAZ
    }
}
0
ответ дан Peter Hall 28 November 2019 в 22:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: