Что такое выражения с побочными эффектами и почему они не должны передаваться в макрос?

я наткнулся на заявление в тексте 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)