Подтвердить что ты не робот

Необходимость скобок в макросах в C

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

#define SQR(x) (x*x)
int main()
{
    int a, b=3;
    a = SQR(b+5);      // Ideally should be replaced with (3+5*5+3), though not sure.
    printf("%d\n",a);
    return 0;
}

Он печатает 23. Если я изменил определение макроса на SQR(x) ((x)*(x)), тогда вывод будет таким, как ожидалось, 64. Я знаю, что вызов макроса в C заменяет вызов с помощью определения макроса, но я все еще не могу понять, как он рассчитал 23.

4b9b3361

Ответ 1

Макросы предварительного процессора выполняют замену текста перед компиляцией кода SQR(b+5) переводит (b + 5 * b + 5) = (6b + 5) = 6 * 3 + 5 = 23

Регулярные вызовы функций будут вычислять значение параметра (b + 3), прежде чем передавать его функции, но поскольку макрос является предварительно скомпилированной заменой, алгебраический порядок операций становится очень важным.

Ответ 2

Потому что (3+5*3+5 == 23).

В то время как ((3+5)*(3+5)) == 64.

Лучший способ сделать это не использовать макрос:

inline int SQR(int x) { return x*x; }

Или просто напишите x*x.

Ответ 3

Рассмотрим замену макроса с помощью этого макроса:

#define SQR(x) (x*x)

Использование b+5 в качестве аргумента. Сделайте замену самостоятельно. В вашем коде SQR(b+5) станет: (b+5*b+5) или (3+5*3+5). Теперь запомните правила правила: * до +. Таким образом, это оценивается как: (3+15+5) или 23.

Вторая версия макроса:

#define SQR(x) ((x) * (x))

Правильно, потому что вы используете parens, чтобы отмахивать ваши аргументы макроса от эффектов приоритета оператора.

Эта страница, объясняющая предпочтение оператора для C, имеет приятный график. Вот соответствующий раздел справочного документа C11.

Здесь следует помнить, что вы должны привыкнуть всегда защищать любые аргументы в своих макросах, используя parens.

Ответ 4

Макрос расширяется до

 a = b+5*b+5;

то есть.

 a = b + (5*b) + 5;

Итак, 23.

Ответ 5

Макрос - это просто прямая замена текста. После предварительной обработки ваш код выглядит следующим образом:

int main()
{
    int a, b=3;
    a = b+5*b+5;
    printf("%d\n",a);
    return 0;
}

Умножение имеет более высокий приоритет оператора, чем добавление, поэтому оно выполняется перед двумя дополнениями при вычислении значения для a. Добавление скобок в определение макроса устраняет проблему, создавая ее:

int main()
{
    int a, b=3;
    a = (b+5)*(b+5);
    printf("%d\n",a);
    return 0;
}

Операции в скобках вычисляются перед умножением, поэтому теперь происходит добавление, и вы получаете результат a = 64, который вы ожидаете.

Ответ 6

После предварительной обработки SQR(b+5) будет расширен до (b+5*b+5). Это, очевидно, неверно.

В определении SQR есть две распространенные ошибки:

  • не заключают аргументы макроса в круглые скобки в макрообъекте, поэтому, если эти аргументы являются выражениями, операторы с разными приоритетами в этих выражениях могут вызвать проблему. Вот версия, которая исправила эту проблему

    #define SQR(x) ((x)*(x))
    
  • оценивать аргументы макроса более одного раза, поэтому, если эти аргументы являются выражениями, которые имеют побочный эффект, этот побочный эффект может быть принят более одного раза. Например, рассмотрим результат SQR(++x).

    Используя расширение GCC typeof, эту проблему можно устранить следующим образом

    #define SQR(x) ({ typeof (x) _x = (x); _x * _x; })
    

Обе эти проблемы могут быть исправлены путем замены этого макроса встроенной функцией

   inline int SQR(x) { return x * x; }

Для этого требуется встроенное расширение GCC или C99, см. 6.40. Встроенная функция работает так же быстро, как макрос.

Ответ 7

Поскольку макросы являются просто заменой строки, и это происходит до завершения процесса. У компилятора не будет возможности увидеть переменную Macro и ее значение. Например: если макрос определяется как

#define BAD_SQUARE(x)  x * x 

и называется так

BAD_SQUARE(2+1) 

компилятор увидит это

2 + 1 * 2 + 1

что приведет, возможно, к неожиданному результату

5

Чтобы исправить это поведение, вы всегда должны окружать макропеременные круглыми скобками, например

#define GOOD_SQUARE(x)  (x) * (x) 

когда этот макрос вызывается, например, как этот

GOOD_SQUARE(2+1)

компилятор увидит это

(2 + 1) * (2 + 1)

что приведет к

9

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

#include <stdio.h>

#define BAD_SQUARE(x)  x * x 
// In macros alsways srround the variables with parenthesis
#define GOOD_SQUARE(x)  (x) * (x) 

int main(int argc, char const *argv[])
{
    printf("BAD_SQUARE(2) = : %d \n", BAD_SQUARE(2) ); 
    printf("GOOD_SQUARE(2) = : %d \n", GOOD_SQUARE(2) ); 
    printf("BAD_SQUARE(2+1) = : %d ; because the macro will be \
subsituted as 2 + 1 * 2 + 1 \n", BAD_SQUARE(2+1) ); 
    printf("GOOD_SQUARE(2+1) = : %d ; because the macro will be \
subsituted as (2 + 1) * (2 + 1) \n", GOOD_SQUARE(2+1) ); 

    return 0;
}

Ответ 8

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

#define SQR(x) ((x)*(x))

Это будет работать для любого аргумента или значения, которое вы передадите.