Max в #define не работает должным образом

Я написал программу следующим образом:

#include<cstdio>
#define max(a,b) a>b?a:b
using namespace std;
int main()
{
 int sum=0,i,k;
 for(i=0;i<5;i++)
 {
 sum=sum+max(i,3);
 }
 printf("%d\n",sum);
 return 0;
}
</cstdio>

Я получил результат: 4

Но когда я сохранил max(i,3) в переменной k, а затем добавлен в sum, я получил правильный вывод:

#include<cstdio>
#define max(a,b) a>b?a:b
using namespace std;
int main()
{
 int sum=0,i,k;
 for(i=0;i<5;i++)
 {
 k=max(i,3);
 sum=sum+k;
 }
 printf("%d\n",sum);
 return 0;
}
</cstdio>

Выход: 16

Может кто-нибудь объяснить, почему это происходит?

5 ответов

Макросы hash-define - это расширение строки, а не "языковая" вещь.

sum=sum+max(i,3);

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

sum=sum+i>3?i:3;

И если вы пишете это без() вокруг него, вы заслуживаете того, чтобы получить неправильный ответ. Попробуйте следующее:

#define max(a,b) (a>b?a:b)

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

#define max(a,b) ((a)>(b)?(a):(b))

но он будет все еще выходить из строя в слишком многих ситуациях, например, аргументы с оценкой побочных эффектов дважды. Вы намного лучше избегаете макросов, где это возможно, и делаете что-то вроде этого:

template <typename t=""> T max(T a, T b) { return a>b?a:b; }
</typename>

или, infact, используя std:: max и std:: min, которые уже были написаны для вас!


Эта строка:

sum=sum+max(i,3);

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

sum = sum + i > 3 ? i : 3;

Что при настройке с помощью парсеров, чтобы сделать его понятным:

sum = (sum + i) > 3 ? i : 3;

Итак, на 5-проходах через цикл выражения:

sum = (0 + 0) > 3 ? 0 : 3; // Result, sum = 3
sum = (3 + 1) > 3 ? 1 : 3; // Result: sum = 3
sum = (3 + 2) > 3 ? 2 : 3; // Result: sum = 3
sum = (3 + 3) > 3 ? 3 : 3; // Result: sum = 3
sum = (3 + 4) > 3 ? 4 : 3; // Result: sum = 4

И это, откуда приходит ваш ответ.

Обычный способ решения этой проблемы - изменить #define на:

#define max(a,b) (((a)>(b))?(a):(b))

Но даже у этого есть некоторые подводные камни.


Я думаю, что у вас проблемы с приоритетом оператора, вы должны помнить, что определение приведет к текстовой замене в исходном коде. Вы должны изменить свое определение на

#define max(a,b) ((a) > (b) ? (a) : (b))


Вывод предшественника (просмотр его с помощью флага -E) будет:

sum = sum+i>3?i:3;

что совпадает с

sum = (sum+i)>3?i:3;

что вы не имели в виду, потому что + имеет более высокий приоритет, чем >. Вы должны использовать:

#define max(a,b) (a>b?a:b)

вместо.


Замена макроса в строке sum=sum+max(i,3); дает следующую форму:

sum=sum+i>3?i:3 ;

который спрашивает, что если sum + i больше, чем 3, чем соответствующее значение суммы. Следовательно, у вас есть 4, потому что каждый раз, когда в цикле происходит новое назначение. Используйте метод шаблона, предложенный Эндрю.

(Петля каждый раз оценивает условие (sum + i) > 3 ? i : 3. Здесь нет кумулятивного добавления.)

licensed under cc by-sa 3.0 with attribution.