Почему оба выхода различны?

Я использовал следующий код C, и он дал результат как обычно:

9, 25

#include int PRODUCT(int x) { return (x*x); } void main() { int i = 3, j, k; j = PRODUCT(i++); k = PRODUCT(++i); printf("%d, %d\n", j,k); } 

Однако, когда я использовал определение макроса вместо функции следующим образом:

 #include #define PRODUCT(x) (x*x) void main() { int i = 3, j, k; j = PRODUCT(i++); k = PRODUCT(++i); printf("%d, %d\n", j,k); } 

выход был

9, 49

Я попытался просмотреть код компиляции с помощью cpp filename.c и расширение main было примерно таким:

 void main() { int i = 3, j, k; j = (i++*i++); k = (++i*++i); printf("%d, %d\n", j,k); } 

Я не понимаю, почему операция приращения выполняется дважды при использовании макросов. Я сам изучаю C, и я не мог понять причину. Заранее благодарю за любую помощь!

    Макросы буквально переписывают вашу исходную программу перед компиляцией. Как вы показываете, ваш код:

     j = (i++*i++); k = (++i*++i); 

    См. Double ++ в каждой строке? Он увеличивается каждый раз каждый раз. С функциями аргумент оценивается один раз, поэтому каждый раз увеличивается каждый раз.

    В макроподстановке использование макроса буквально заменяется содержимым макроса перед компиляцией.

    Я хотел бы привести другой пример того, как это может пойти ужасно неправильно, если его неправильно использовать. Надеюсь, это также показывает, что я (и другие ответы) подразумевал под « буквально замененным»,

    Вы написали:

     #define PRODUCT(x) (x*x) 

    Если вы это сделаете:

     PRODUCT(a + b) 

    Это становится, после подстановки:

     (a + b*a + b) 

    Это не то же самое, что:

     (a+b) * (a+b) 

    Операция increment выполняется дважды, потому что макрос является более или менее текстовой заменой, как вы обнаружили. Поэтому любой аргумент макроса, который используется несколько раз, будет оцениваться несколько раз.

    Этот ответ SO содержит несколько советов, которые могут помочь избежать этой проблемы.

    Ответ находится прямо в вашем последнем примере.

     void main() { int i = 3, j, k; j = (i++*i++); k = (++i*++i); printf("%d, %d\n", j,k); } 

    Рассмотрим этот вариант:

     i = 3, i = 3 3 x 3 = 9 i++ = 4 i++ = 5 ++i = 6 ++i = 7 7 x 7 = 49. 

    После оценки i ++ или ++ i новое значение i будет одинаковым в обоих случаях . Разница между предварительным приращением и пост-приращением заключается в результате оценки в выражении.

    i ++ оценивает старое значение i и увеличивает i.

    ++ i увеличивает i и оценивает новое значение i.

    В вашем коде,

    j = (i ++ * i ++);

    Выражение оценивает старое значение i (т. Е. 3) и 1-го i ++ приращения от i до 4 и 2-го i ++ с шагом i до 5

    Ваше первое утверждение оценивает

    j = 3 * 3;

    Полученное значение j равно 9.

    После этого утверждения значение i равно 5 .

    Затем,

    k = (++ i * ++ i);

    1st ++ i увеличивает i до 6, а второй i ++ увеличивается с i до 7, а выражение оценивает новое значение i (т.е.) 7.

    Ваше второе утверждение оценивает

    k = 7 * 7;

    Результирующее значение k равно 49 .

    См. Эту ссылку: http://en.wikipedia.org/wiki/Increment_and_decrement_operators

    Он делает это, потому что, когда препроцессор выполняет макрораспределение, вы можете рассматривать его так, как если бы он делал замену текста перед компиляцией. Каждый раз, когда он видит x , он заменяет его тем, что вы там кладете. Итак, x -> i++ или ++i . Вот почему приращения происходят дважды.