Почему тернарный оператор используется для определения 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, так и кажется, что они равны. Есть причина, чтобы написать код, как они это сделали?

7 79

7 ответов:

вы правы, В С это тавтологично. Оба ваших конкретных троичных условных и(1 > 0) типа int.

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

Я предполагаю, что автор вложил в это некоторую мысль, с целью сохранения C++ совместимость.

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

не называть имен и показывать пальцем, но ПК-Линт-это такой инструмент пылеобразования.

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

вы иногда будете видеть это в очень старый код, прежде чем был стандарт 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 в конце все из них, чем беспокоиться о том, какие из них действительно нужны.

в коде 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 '{'**

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

ConditionalExpr ? 1 : 0

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

в C++, это фактически приведение к int, потому что условные выражения в C++ есть тип 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
*/

}

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

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

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

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

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