为什么三元运算符用于在宏中定义 1 和 0?

Why is the ternary operator used to define 1 and 0 in a macro?

我正在为嵌入式项目使用 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,看来是相等的。是否有理由像他们那样编写代码?

你是对的,在C语言中是同义反复。您的特定三元条件 (1 > 0) 都是 int.

类型

但它 在 C++ 中很重要,但在一些奇怪的极端情况下(例如作为重载函数的参数),因为你的三元条件表达式是 int 类型, 而 (1 > 0) 的类型是 bool.

我的猜测是作者对此进行了一些思考,着眼于保持 C++ 兼容性。

有 linting 工具认为比较的结果是布尔值,不能直接用于算术。

不是指名或指责,而是 PC-lint is such a linting tool

我并不是说他们是对的,但可以解释为什么代码是这样写的。

在 C 中没关系。 C 中的布尔表达式具有 int 类型和一个 01 的值,因此

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

}

也有可能没有效果,作者只是觉得这样让代码更清晰。

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 '{'**

一个简单的解释是有些人要么不理解条件在 C 中会 return 相同的值,要么他们认为写 ((a>b)?1:0).[=12 更简洁=]

这解释了为什么有些人也在具有适当布尔值的语言中使用类似的结构,在 C 语法中将是 (a>b)?true:false)

这也解释了为什么您不应该不必要地更改此宏。

您有时会在 非常古老的 代码中看到这一点,在有 C 标准明确指出 (x > y) 的计算结果为数字 1 或 0 之前;一些 CPU 宁愿将其计算为 −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 的末尾更易于维护他们比担心哪些人真正需要它。

也许,作为一个嵌入式软件,会提供一些线索。也许有许多使用这种风格编写的宏,以轻松暗示 ACTI 行使用直接逻辑而不是反向逻辑。