Необходимость в скобках в макросах в 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; }