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

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

本文关键字:定义 运算符 三元 为什么      更新时间:2023-10-16

我正在为嵌入式项目使用 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 就是这样一个 linting 工具。

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

你有时会在非常古老的代码中看到这一点,在有 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放在所有这些表达式的末尾比担心哪些真正需要它更易于维护。

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 中没关系。 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
*/
}

也可能没有意图的效果,作者只是认为它使代码更清晰。

一个简单的解释是,有些人要么不理解条件会在 C 中返回相同的值,要么他们认为写((a>b)?1:0)更干净。

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

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

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