Почему тернарный оператор используется для определения 1 и 0 в макросе?

Я использую SDK для встроенного проекта. В этом исходном коде я нашел код, который, по крайней мере, я нашел особенным. Во многих местах в SDK есть исходный код в этом формате:

#define ATCI_IS_LOWER( alpha_char )  ( ( (alpha_char >= ATCI_char_a) && (alpha_char <= ATCI_char_z) ) ? 1 : 0 )

#define ATCI_IS_UPPER( alpha_char )  ( ( (alpha_char >= ATCI_CHAR_A) && (alpha_char <= ATCI_CHAR_Z) ) ? 1 : 0 )

Используется ли здесь использование тернарного оператора?

Не

#define FOO (1 > 0)

то же, что и

#define BAR ( (1 > 0) ? 1 : 0)

?

Я попытался оценить его, используя

printf("%d", FOO == BAR);

и получить результат 1, так что кажется, что они равны. Есть ли причина писать код, как они?

Ответ 1

Вы правы, в C он тавтологичен. Оба ваших тернарных условных выражения и (1 > 0) имеют тип int.

Но это имело бы значение в С++, хотя в некоторых любопытных случаях (например, в качестве параметров для перегруженных функций), поскольку ваше тернарное условное выражение имеет тип int, тогда как (1 > 0) имеет тип bool.

Мое предположение заключается в том, что автор задумался над этим, имея в виду сохранение совместимости с С++.

Ответ 2

Есть инструменты для переливания, которые считают, что результат сравнения является логическим и не может использоваться непосредственно в арифметике.

Не называть имена и не указывать пальцы, но PC-lint - это инструмент для литья.

Я не говорю, что они правы, но это возможное объяснение того, почему код был написан так.

Ответ 3

Иногда вы увидите это в очень старом коде, прежде чем появился стандарт C, чтобы указать (x > y) на числовые 1 или 0; некоторые процессоры скорее предпочли бы оценивать -1 или 0 вместо этого, и некоторые очень старые компиляторы, возможно, просто следовали вместе, поэтому некоторые программисты чувствовали, что им нужна дополнительная защита.

Иногда вы также увидите это, потому что похожие выражения не обязательно оценивают числовые 1 или 0. Например, в

#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) ? 1 : 0)

внутреннее выражение & оценивает либо 0, либо числовое значение F_DO_GRENFELZ, что, вероятно, не равно 1, поэтому ? 1 : 0 служит для канонизации. Я лично считаю, что более ясно написать, что

#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) != 0)

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

Ответ 4

В коде SDK есть ошибка, и троярь, вероятно, был kludge, чтобы исправить его.

Будучи макросом, аргументы (alpha_char) могут быть любым выражением и должны быть заключены в круглые скобки, поскольку такие выражения, как 'A' && "c" завершит тест.

#define IS_LOWER( x ) ( ( (x >= 'a') && (x <= 'z') ) ?  1 : 0 )
std::cout << IS_LOWER('A' && 'c');
**1**
std::cout << IS_LOWER('c' && 'A');
**0**

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

Итак, в вашем примере (но с параметрами) они оба прослушиваются.

#define FOO(x) (x > 0)
#define BAR(x) ((x > 0) ? 1 : 0)

Они были бы правильно заменены на

#define BIM(x) ((x) > 0)

@CiaPan Делает замечательный момент в следующем комментарии, который заключается в том, что использование параметра несколько раз приводит к неопределенным результатам. Например,

#define IS_LOWER( x ) (((x) >= 'a') && ((x) <= 'z'))
char ch = 'y';
std::cout << IS_LOWER(ch++);
**1** 
**BUT ch is now '{'**

Ответ 5

В C это не имеет значения. Булевы выражения в C имеют тип int и значение, которое либо 0, либо 1, поэтому

ConditionalExpr ? 1 : 0

не имеет эффекта.

В С++ он эффективно применяется к int, потому что условные выражения в С++ имеют тип bool.

#include <stdio.h>
#include <stdbool.h>

#ifndef __cplusplus

#define print_type(X) _Generic(X, int: puts("int"), bool: puts("bool") );

#else
template<class T>
int print_type(T const& x);
template<> int print_type<>(int const& x) { return puts("int"); }
template<> int print_type<>(bool const& x) { return puts("bool"); }


#endif

int main()
{
    print_type(1);
    print_type(1 > 0);
    print_type(1 > 0 ? 1 : 0);

/*c++ output:
  int 
  int 
  int

  cc output:
  int
  bool
  int
*/

}

Также возможно, что никакого эффекта не было, и автор просто подумал, что он сделал код более понятным.

Ответ 6

Одно простое объяснение состоит в том, что некоторые люди либо не понимают, что условие возвратит одно и то же значение в C, либо они считают, что писать ((a>b)?1:0) чище.

Это объясняет, почему некоторые из них также используют аналогичные конструкции на языках с правильными логическими значениями, которые в C-синтаксисе будут (a>b)?true:false).

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

Ответ 7

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