Можно ли использовать оператор if внутри #define?

Я пытаюсь сделать макрос со следующей формулой: (a^2/(a+b))*b, и я хочу убедиться, что не будет деления на ноль.

#define SUM_A( x, y ) if( x == 0 || y == 0) { 0 } else { ( ( ( x * x ) / ( ( x ) + ( y ) ) ) * ( y ) )}

и затем я вызываю макрос внутри main:

float a = 40, b = 10, result; 
result = SUM_A(a, b); 
printf("%f", result);

Я попытался использовать скобки вокруг функции if, но я продолжаю получать синтаксические ошибки перед оператором if. Я также пытался использовать return, но я где-то читал, что вы не должны использовать это в определении.

Ответ 1

Вы не можете использовать оператор if, потому что #define интерпретируется препроцессором, а вывод будет

 result=if( x == 0 || y == 0) { 0 } else { ( ( ( x * x ) / ( ( x ) + ( y ) ) ) * ( y ) )}

что является неправильным синтаксисом.

Но альтернативой является использование тернарного оператора. Измените

#define SUM_A( x, y )  ((x) == 0 || (y) == 0 ? 0 : ( ( ( (x) * (x) ) / ( ( x ) + ( y ) ) ) * ( y ) ))

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

Ответ 2

if вводит оператор, а не выражение. Используйте оператор "тройной" (условный):

#define SUM_A(x, y) (((x) == 0 || (y) == 0)? 0: ((((x) * (x)) / ((x) + (y))) * (y)))

В качестве альтернативы сделайте это inline функцией:

inline float sum_a(float x, float y)
{
    if (x == 0 || y == 0)
        return 0;
    else
        return ((x * x) / (x + y)) * y;
}

Это позволяет избежать проблемы множественной оценки x и/или y и гораздо более читабельна, но она фиксирует типы x и y. Вы также можете удалить inline строку и позволить компилятору решить, стоит ли делать эту функцию полезной (inline функция не гарантирует, что она будет выполнять inlining).

Ответ 3

Технически можно использовать операторы if в #define (но не так, как вы ожидали). Поскольку #define - это просто замена текста, вы должны быть очень осторожны, как вы его расширяете. Я обнаружил, что это работает...

#define SUM_A(x, y)                                        \
({                                                         \
    double answer;                                         \
    if ((x) == 0 || (y) == 0)                              \
        answer = 0;                                        \
    else                                                   \
        answer = ((double)((x)*(x)) / ((x)+(y))) * (y);    \
    (answer);                                              \
})
// Typecasting to double necessary, since int/int == int in C

Это должно дать вам результат, который вы ищете, и нет причин, по которым он не может быть расширен, чтобы включить в него несколько else if (хотя, как указывали другие ответы, возможно, проще использовать тернарный оператор).

Ответ 4

Проблема в том, что оператор if не является выражением и не возвращает значение. Кроме того, нет веских оснований для использования макроса в этом случае. На самом деле это может привести к очень серьезным проблемам с производительностью (в зависимости от того, что вы передаете в качестве аргументов макроса). Вместо этого вы должны использовать функцию.

Ответ 5

Есть несколько проблем с вашим макросом:

  • он расширяется до утверждения, поэтому вы не можете использовать его как выражение

  • аргументы в расширении не указаны в скобках: вызов этого макроса с чем-либо, кроме имен переменных или констант, вызовет проблемы.

  • аргументы оцениваются несколько раз: если вы вызываете макрос с аргументами, которые имеют побочные эффекты, такие как SUM_A(a(), b()) или SUM_A(*p++, 2), побочный эффект будет происходить несколько раз.

Чтобы избежать всех этих проблем, используйте функцию, определенную как static inline чтобы оптимизировать компилятор (это необязательно, и современные компиляторы делают это автоматически):

static inline int SUM_A(float x, float y) {
    if (x == 0 || y == 0)
        return 0; 
    else
        return x * x / (x + y) * y;
}

Заметки:

  • эта функция использует арифметику с плавающей запятой, которую макрос не обязательно, в зависимости от фактических типов ее аргументов.
  • тест не предотвращает деление на ноль: SUM_A (-1, 1) все еще выполняет одно.
  • деление на ноль не обязательно является проблемой: с аргументами с плавающей запятой он создает бесконечность или NaN, а не ошибку времени выполнения.

Ответ 6

ДА, вы можете иметь оператор if в макросе. Вам нужно отформатировать его правильно. Вот пример:

#define MY_FUNCTION( x )  if( x ) { PRINT("TRUE"); } else { PRINT("FALSE"); }