Что такое выражения с побочными эффектами и почему они не должны передаваться в макрос?
я наткнулся на заявление в тексте C как программировать:
"выражения с побочными эффектами (т. е. изменяются значения переменных) не должны передаваться в макрос, поскольку аргументы макроса могут оцениваться более одного раза.".
мой вопрос в том, что такое выражения с побочными эффектами и почему они не должны передаваться в макрос?
3 ответов
классический пример-макрос для вычисления максимального из двух значений:
#define MAX(a, b) ((a) > (b) ? (a) : (b))
теперь давайте "вызовем" макрос следующим образом:
int x = 5;
int y = 7;
int z = MAX(x++, y++);
теперь, если MAX
была нормальной функцией, мы ожидали бы, что x
и y
будет увеличено один раз, верно? Однако, поскольку это макрос, "вызов" заменяется следующим образом:
int z = ((x++) > (y++) ? (x++) : (y++));
Как видите, переменная y
будет увеличиваться два раза, раз в состоянии и раз как конечный результат тернарный оператор.
это результат выражения с побочными эффектами (выражение после увеличения) и расширения макроса.
в соответствующей заметке есть и другие опасности с макросами. Например, возьмем этот простой макрос:
#define MUL_BY_TWO(x) (x * 2)
выглядит просто право? Но теперь что, если мы используем его так:
int result = MUL_BY_TWO(a + b);
это будет расширяться как
int result = (a + b * 2);
и как ты надеюсь, знает, что умножение имеет более высокий приоритет, чем сложение, поэтому выражение a + b * 2
эквивалентно a + (b * 2)
, вероятно, не то, что предполагал макро-писатель. Вот почему аргументы макросов должны быть помещены в собственные круглые скобки:
#define MUL_BY_TWO(x) ((x) * 2)
тогда расширение будет
int result = ((a + b) * 2);
это, наверное, правильно.
проще говоря, побочный эффект-это запись в объект или чтение Летучего объекта.
Итак, пример побочного эффекта:
i++;
вот использование побочного эффекта в макросе:
#define MAX(a, b) ((a) > (b)) ? (a) : (b))
int a = 42;
int b = 1;
int c;
c = MAX(a, b++);
опасность противоречит функции, где аргументы передаются по значению, которое вы потенциально изменяете b
объект один или два раза (в зависимости от аргументов макроса, здесь один раз) в макросе из-за способа работы макросов (путем замены b++
маркеры в определении макроса).
побочные эффекты можно определить как:
оценка выражения производит что-то, и если, кроме того, происходит изменение состояния среды выполнения, говорят, что выражение (его оценка) имеет некоторый побочный эффект(ы). Например:
int x = y++; //where y is also an int
в дополнение к операции инициализации значение y изменяется из-за побочного эффекта оператора++.
теперь рассмотрим макрос для возведения в квадрат целых чисел :
#define Sq(a) a*a
main()
{
int a=6;
int res=Sq(a);
printf("%d\n",res);
res=Sq(++a);
printf("%d",res);
}
вы ожидаете, что выход будет
36
49
36
64
потому что макрос приводит к текстовой замене и
res становится (++a)*(++a) Я. e, 8*8=64
поэтому мы не должны передавать аргументы с побочными эффектами макросы. (http://ideone.com/mMPQLP)