Вопрос по c – Необходимость в скобках в макросах на С [дубликат]

26

This question already has an answer here:

C macros and use of arguments in parentheses 2 answers

Я пытался играть с определением макроса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.

Nice answer to a duplicate Palec
Связанные с:When can the parentheses around arguments in macros be omitted? Palec
В твоей будущей карьере. Старайтесь вообще не полагаться на макросы. Или, если вам нужно их использовать, используйте их для очень маленьких кусочков кода. Не огромные макро-чудовища, которые занимают половину страницы. C Johnson
Связанные с:Can we remove parentheses around arguments in C macros definitions? Palec
@CJohnson: Да, я с трудом осознал необходимость использования макроса в моих проектах на C / C ++, это одна из запутанных областей C, даже после того, как я несколько лет работал разработчиком / студентом. Kushal

Ваш Ответ

8   ответов
2

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

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

  1. do not enclose arguments of macro in parentheses in the macro body, so if those arguments are expressions, operators with different precedences in those expressions may cause problem. Here is a version that fixed this problem

    #define SQR(x) ((x)*(x))
    
  2. evaluate arguments of macro more than once, so if those arguments are expressions that have side effect, those side effect could be taken more than once. For example, consider the result of SQR(++x).

    By using GCC typeof extension, this problem can be fixed like this

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

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

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

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

6

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

 a = b+5*b+5;

то есть

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

Итак, 23

2

й обработки ваш код выглядит так:

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 результат, который вы ожидаете.

10

(3+5*3+5 == 23).

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

Лучший способ сделать этоnot to use a macro:

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

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

-2

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

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

Без объяснения, без ответа на вопрос (how расширение макросов работает), неправильное форматирование, нет добавленной стоимости. Поэтому нет никаких оснований для этого ответа.
7

#define SQR(x) (x*x)

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

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

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

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

Эта страница объяснение предпочтения оператора для C имеет хороший график.Здесь & APOS; s соответствующий раздел справочного документа C11.

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

30

как код скомпилирован SQR(b+5) переводит на (b + 5 * b + 5) = (6b + 5) = 6 * 3 + 5 = 23

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

0

и это происходит до завершения процесса. Компилятор не сможет увидеть переменную 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;
}

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