为什么使用三元运算符在宏中定义 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 就是这样一个 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
和一个值为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
*/
}
也可能没有意图的效果,作者只是认为它使代码更清晰。
一个简单的解释是,有些人要么不理解条件会在 C 中返回相同的值,要么他们认为写((a>b)?1:0)
更干净。
这就解释了为什么有些人在具有适当布尔值的语言中使用类似的结构,这在 C 语法中是(a>b)?true:false)
。
这也解释了为什么您不应该不必要地更改此宏。
也许,作为一个嵌入式软件,会提供一些线索。也许有很多宏是用这种风格编写的,以方便提示ACTI行使用直接逻辑而不是反转逻辑。
- 在 C++ 的自定义运算符中删除与删除[](不同于常见的删除与删除[]问题)
- 自定义运算符重载C++,无开销
- 如何为缺少预定义运算符而不扩展命名空间"std"的标准类型定义运算符>> (istream &, ...)?
- 如何使用C++将MXNET自定义运算符构建到单独的库/包中?
- 模板类的用户定义运算符上的 C++ 隐式转换
- 什么是编程语言支持定义您自己的自定义运算符?
- 如何在Qt中为矩阵类定义[ ][ ]运算符?
- 在 c++ 迭代器中,我应该同时定义运算符== 和运算符!=吗?
- 在 rxcpp 中创建自定义运算符
- 错误 C2676;在C++的二叉搜索树类中定义 ++ 运算符时遇到问题
- std::map :使用自定义运算符时更新密钥
- 最初定义运算符C++在哪里
- 如何在Tensorflow Lite中添加自定义运算符
- 如何在 rxcpp 自定义运算符中正确推断泛型
- 在模板化类之外定义运算符重载
- 为类定义之外的模板类定义运算符[]()(数组订阅)
- 是否可以在类定义之外定义运算符[]()(数组订阅)
- C++ 重新定义运算符<() 和运算符!=()
- 如何将上下文信息传递给自定义运算符<<适用于 std::iostream
- 无法在C++中定义++运算符,这里有什么问题?