为什么在宏中使用三元运算符来定义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,看起来它们是相等的。有什么理由要像他们那样写代码吗?

10670 次浏览

你是对的,在 C 语言中它是同义的。你的特殊的三元条件 还有 (1 > 0)都是 int类型。

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

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

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

不是指名道姓,也不是指责别人,而是 PC 线头就是这样一个线头工具

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

在 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 代码中有一个 bug,而三进制代码可能是用来修复它的一个组件。

作为一个宏,参数(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 中返回相同的值,要么认为编写 ((a>b)?1:0)更简单。

这就解释了为什么有些语言中也使用类似的结构和适当的布尔值,在 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放在 所有的末尾比担心哪些表达式真正需要它更容易维护。

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