我可以使用按位运算符而不是逻辑运算符吗?
Can I use bitwise operators instead of logical ones?
按位运算符作用于位,逻辑运算符计算布尔表达式。只要表达式returnbool
,我们为什么不使用位运算符而不是逻辑运算符呢?
在此示例中,我使用按位而不是逻辑:
#include <iostream>
int main(){
int age;
std::cin >> age;
if( (age < 0) | (age > 100) ) // eg: -50: 1 | 0 = 1
std::cout << "Invalid age!" << std::endl;
// if( (age < 0) || (age > 100) )
// std::cout << "Invalid age!" << std::endl;
return 0;
}
即使使用位运算符达到相同的结果,出于性能原因,这里最好使用逻辑运算符。
在表达式 (age < 0) || (age > 100)
中,仅当 (age < 0)
为 false
时,才会计算第二个条件 (age > 100)
。对于这样的表达式,编译器会生成如下代码:
cmpl [=10=]x0,-0x4(%rbp)
js 1004010f9 <main+0x19> // <-- Skip right expr evaluation if left true
cmpl [=10=]x64,-0x4(%rbp)
jle 100401100 <main+0x20>
||
不会产生任何额外的分支来跳过第二个表达式求值。
你不应该。假设您收到两个值,只有当它们都非零时才允许您继续。
int b = foo(1); // returns 0x1
int c = foo(2); // returns 0x2
比以下条件产生以下结果:b && c == true
,而 b & c == 0
if (b && c)
{
// This block will be entered
}
if (b & c)
{
// This block won't
}
一个可能的答案是:优化。例如:
if ((age < 0) | (age > 100))
假设 age = -5
,由于满足第一个条件 (-5<0
),因此无需计算 (age > 100)
。但是,前面的代码会计算 (age > 100)
表达式,这不是必需的。
与:
if ((age < 0) || (age > 100))
只有第一部分会被评估。
注意:正如评论中提到的@Lundin,有时|
比||
快第二个选项的分支准确性(以及预测错误的问题)。所以在其他表达式如此便宜的情况下, |选项 可能 会更快。因此,了解这些情况的唯一方法是在目标平台上对代码进行基准测试。
最重要的答案是避免未定义的行为和错误:
你可以想象这段代码:
int* int_ptr = nullptr;
if ((int_ptr != nullptr) & (*int_ptr == 5))
此代码包含未定义的行为。但是,如果将 &
替换为 &&
,则不再存在未定义的行为。
答案是肯定的,你可以。问题是你为什么要这样做?我可以列举几个你不应该的理由:
- 其他程序员可能会很困惑。
- 很容易忽略其中一个操作数不是
bool
类型,这会导致细微的错误。
- 操作数计算的顺序未定义。
- 它打乱了短路规则。
为了说明最后一点:
bool f1() {
cout << "f1" << endl;
return true;
}
bool f2() {
cout << "f2" << endl;
return true;
}
int main() {
if (f1() || f2()) {
cout << "That was for ||" << endl;
}
if (f1() | f2()) {
cout << "That was for |" << endl;
}
return 0;
}
它打印:
f1
That was for ||
f1
f2
That was for |
假设 f2
可能有显着的副作用 (if (okToDestroyWorld && destroyWorld()) ...
),差异可能是巨大的。 Java 程序员(其中 |
和 ||
实际上是由语言规范为布尔值定义的)不会感到惊讶,但这不是 C++ 中的通常做法。
我只能想到对布尔值使用按位运算符的一个原因:如果您需要 XOR。没有 ^^
运算符,所以只要 a
和 b
都是 bool
并且不涉及副作用,if (a ^ b)
就可以了。
||
和 |
之间有明显的区别。
与语言中的大多数其他运算符不同,逻辑运算符 ||
明确指定求值顺序。 ||
的第一个操作数必须在第二个之前计算。第二个根本不需要评估。
这与大多数运算符的行为方式 |
根本不同:计算顺序未指定,两个表达式都将被计算。即使在发现一个操作数不为零的情况下,仍将评估另一个操作数的副作用。
意味着像 f1() || f2()
这样的代码将始终计算为这个伪代码:
if(f1() != 0)
{
f2();
}
而 f1() | f2()
将以程序员无法知道的未指定顺序执行这两个函数。
这也意味着像 "|| 比 |" 这样的陈述是幼稚的。当然,在 ||
的情况下,第二个操作数不一定被评估,但这是以分支为代价的,以及对允许编译器如何重新排序表达式的限制。哪个运算符通常更快并不明显。
按位运算符作用于位,逻辑运算符计算布尔表达式。只要表达式returnbool
,我们为什么不使用位运算符而不是逻辑运算符呢?
在此示例中,我使用按位而不是逻辑:
#include <iostream>
int main(){
int age;
std::cin >> age;
if( (age < 0) | (age > 100) ) // eg: -50: 1 | 0 = 1
std::cout << "Invalid age!" << std::endl;
// if( (age < 0) || (age > 100) )
// std::cout << "Invalid age!" << std::endl;
return 0;
}
即使使用位运算符达到相同的结果,出于性能原因,这里最好使用逻辑运算符。
在表达式 (age < 0) || (age > 100)
中,仅当 (age < 0)
为 false
时,才会计算第二个条件 (age > 100)
。对于这样的表达式,编译器会生成如下代码:
cmpl [=10=]x0,-0x4(%rbp)
js 1004010f9 <main+0x19> // <-- Skip right expr evaluation if left true
cmpl [=10=]x64,-0x4(%rbp)
jle 100401100 <main+0x20>
||
不会产生任何额外的分支来跳过第二个表达式求值。
你不应该。假设您收到两个值,只有当它们都非零时才允许您继续。
int b = foo(1); // returns 0x1
int c = foo(2); // returns 0x2
比以下条件产生以下结果:b && c == true
,而 b & c == 0
if (b && c)
{
// This block will be entered
}
if (b & c)
{
// This block won't
}
一个可能的答案是:优化。例如:
if ((age < 0) | (age > 100))
假设 age = -5
,由于满足第一个条件 (-5<0
),因此无需计算 (age > 100)
。但是,前面的代码会计算 (age > 100)
表达式,这不是必需的。
与:
if ((age < 0) || (age > 100))
只有第一部分会被评估。
注意:正如评论中提到的@Lundin,有时|
比||
快第二个选项的分支准确性(以及预测错误的问题)。所以在其他表达式如此便宜的情况下, |选项 可能 会更快。因此,了解这些情况的唯一方法是在目标平台上对代码进行基准测试。
最重要的答案是避免未定义的行为和错误:
你可以想象这段代码:
int* int_ptr = nullptr;
if ((int_ptr != nullptr) & (*int_ptr == 5))
此代码包含未定义的行为。但是,如果将 &
替换为 &&
,则不再存在未定义的行为。
答案是肯定的,你可以。问题是你为什么要这样做?我可以列举几个你不应该的理由:
- 其他程序员可能会很困惑。
- 很容易忽略其中一个操作数不是
bool
类型,这会导致细微的错误。 - 操作数计算的顺序未定义。
- 它打乱了短路规则。
为了说明最后一点:
bool f1() {
cout << "f1" << endl;
return true;
}
bool f2() {
cout << "f2" << endl;
return true;
}
int main() {
if (f1() || f2()) {
cout << "That was for ||" << endl;
}
if (f1() | f2()) {
cout << "That was for |" << endl;
}
return 0;
}
它打印:
f1
That was for ||
f1
f2
That was for |
假设 f2
可能有显着的副作用 (if (okToDestroyWorld && destroyWorld()) ...
),差异可能是巨大的。 Java 程序员(其中 |
和 ||
实际上是由语言规范为布尔值定义的)不会感到惊讶,但这不是 C++ 中的通常做法。
我只能想到对布尔值使用按位运算符的一个原因:如果您需要 XOR。没有 ^^
运算符,所以只要 a
和 b
都是 bool
并且不涉及副作用,if (a ^ b)
就可以了。
||
和 |
之间有明显的区别。
与语言中的大多数其他运算符不同,逻辑运算符 ||
明确指定求值顺序。 ||
的第一个操作数必须在第二个之前计算。第二个根本不需要评估。
这与大多数运算符的行为方式 |
根本不同:计算顺序未指定,两个表达式都将被计算。即使在发现一个操作数不为零的情况下,仍将评估另一个操作数的副作用。
意味着像 f1() || f2()
这样的代码将始终计算为这个伪代码:
if(f1() != 0)
{
f2();
}
而 f1() | f2()
将以程序员无法知道的未指定顺序执行这两个函数。
这也意味着像 "|| 比 |" 这样的陈述是幼稚的。当然,在 ||
的情况下,第二个操作数不一定被评估,但这是以分支为代价的,以及对允许编译器如何重新排序表达式的限制。哪个运算符通常更快并不明显。