Почему результат отличается в этих двух сценариях - PullRequest
0 голосов
/ 12 мая 2019

Пожалуйста, дайте мне полное описание .... Первый фрагмент кода имеет «вызов функции» (вызов макроса) перед оператором приращения, а второй - вызов функции после оператора приращения.

  1. #include <stdio.h>
    #define square(x) x*x
    int main()
    {
      int a,b=3;
      a=square (b)++;
      printf("%d%d",a,b);
      return 0;
    }
    

    вывод:

    124
    

    почему здесь возвращается 124

  2. #include <stdio.h>
    #define square(x) x*x
    int main()
    {
      int a,b=3;
      a=square (b++);
      printf("%d%d",a,b);
      return 0;
    }
    

    вывод:

    125
    

    а 125 тут?

Ответы [ 2 ]

3 голосов
/ 12 мая 2019

Следует помнить, что макросы обеспечивают простую замену токенов препроцессора. В частности, они могут оценивать свои аргументы более одного раза и, если они не защищены скобками, они могут привести к непреднамеренной повторной ассоциации.

В первом примере мы имеем

a=square (b)++;

Это расширяется до:

a=b*b++;

Это фактически неопределенное поведение, поскольку b и b++ не секвенированы, а b++ изменяет b. В вашем случае вы видите 12 и 4 для a и b, поэтому может показаться, что первое значение b - это увеличенное значение, так что вы получаете 4 * 3, но вы можете не рассчитывайте на это поведение. Конечное значение b равно 4, поскольку оно увеличивается один раз.

Во втором примере имеем:

a=square (b++);

Это расширяется до:

a=b++*b++;

Это опять неопределённое поведение. В вашем случае кажется, что вы получаете 4 * 3 (или 3 * 4), но опять же, вы не можете рассчитывать на это поведение. Окончательное значение b равно 5, поскольку оно увеличивается вдвое, но это тоже неопределенное поведение.

0 голосов
/ 13 мая 2019

В дополнение к ответу Тома, который объясняет, что происходит, вот пример того, как вы можете определить макрос для безопасного возведения в квадрат числа:

#define SQR(x) (                    \
{                                   \
        __auto_type x_  = (x);      \
                                    \
        x_ * x_;                    \
}                                   \
)

Он имеет только вид xи поэтому он не оценивает его дважды.Вместо этого используется копия x_.Обратите внимание, что переменные, созданные в макросе, могут конфликтовать с другими переменными, созданными в функции, которая вызывает макрос.Чтобы избежать конфликтов имен, вы используете специальные имена, которые не должны использоваться в обычном коде, такие как конечный _.

С этим макросом это:

a = SQR(b++);

будет эквивалентноthis:

a = SQR(b);
b++;

Предупреждение: на некоторых компиляторах это работает как расширение (например, GCC), но это не стандартная C.

Другой вариант, если вы хотите стандартную C, этоиспользовать встроенную функцию.Это нормально, если вы хотите, чтобы он работал только с одним типом (в C11 есть _Generic, но я никогда не использовал его, так что понятия не имею).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...