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