Необходимость в скобках в макросах в 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("%dn",a);
return 0;
}
Он печатает 23
. Если я изменю определение макроса на SQR(x) ((x)*(x))
, то результат будет таким, как и ожидалось, 64
. Я знаю, что вызов макроса в C заменяет вызов определением макроса, но я до сих пор не могу понять, как он вычисляется 23
.
8 ответов:
Макросы препроцессора выполняют замену текста перед компиляцией кода таким образом, что
Регулярные вызовы функций вычисляют значение параметра (b+3) перед передачей его функции, но поскольку макрос является предварительно скомпилированной заменой, алгебраический порядок операций становится очень важным.SQR(b+5)
переводится в (b+5*b+5) = (6b+5) = 6*3+5 = 23
Потому что
(3+5*3+5 == 23)
.Тогда как
((3+5)*(3+5)) == 64
.Лучший способ сделать это - не использовать макрос :
inline int SQR(int x) { return x*x; }
Или просто напишите
x*x
.
Рассмотрим замену макроса с помощью этого макроса:
#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.
Макрос - это просто прямая текстовая подстановка. После предварительной обработки код выглядит следующим образом:
Умножение имеет более высокий приоритет оператора, чем сложение, поэтому оно выполняется перед двумя сложениями при вычислении значения для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
, который вы ожидать.
После предварительной обработки,
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 встроенная функция так же быстра, как макрос.
Потому что макросы - это просто замена строки,и это происходит до завершения процесса. Компилятор не сможет увидеть макропеременную и ее значение. Например: если макрос определен как
#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; }