it-roy-ru.com

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

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

79
Viktor S

Вы правы, в C это тавтологично. И ваши конкретные троичные условные и (1 > 0) имеют тип int.

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

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

132
Bathsheba

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

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

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

29
unwind

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

20
zwol

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

Будучи макросом, аргументы (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 '{'**
16
Konchog

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

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

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

5
Hans Olsson

В 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
*/

}

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

5
PSkocik

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

0
J.Guarin