按位运算符的操作顺序(a & (a=1) 奇怪的行为)?
Order of operations for bitwise operators (a & (a=1) strange behavior)?
在得知不能在标准中递增布尔值后,我正在处理一些 C++ 代码。我认为递增布尔值会很有用,因为如果可以的话我喜欢压缩函数并且
unsigned char b = 0; //type doesn't really matter assuming no overflow
while (...) {
if (b++) //do something
...}
有时很有用,但不用担心整数溢出会很好。由于布尔值不能取除 0 或 1 以外的任何值,我认为这也许可行。当然,您可以在执行操作后将布尔值分配给 1,但这需要额外的代码行。
无论如何,我考虑了如何以不同的方式实现这一点,然后想到了这个。
bool b = 0;
while (...)
if (b & (b=1)) ...
即使在第一次通过时,结果总是评估为真。我想 - 当然,它只是从右到左执行按位运算符,除了当我交换顺序时,它做了同样的事情。
所以我的问题是,这个表达式是如何计算的,以便它简化为始终为真?
顺便说一句,我想做我想做的事情的方法是这样的:
bool b = 0;
while (...) if (!b && !(b=1)) //code executes every time except the first iteration
还有一种方法可以让它只执行一次。
或者只做实际可读且明显的解决方案。
bool b = 0;
while (...) {
if (b) {} else {b=1;}
}
这就是 std::exchange
的用途:
if (std::exchange(b, true)) {
// b was already true
}
如前所述,排序规则意味着 b & (b=1)
在 C 和 C++ 中都是 undefined behaviour。您不能修改变量并“在没有适当分离的情况下”从中读取(例如,作为单独的语句)。
我不明白您可以赋予“按位递增”运算符什么含义。如果我是对的,你所实现的只是设置低阶位,这是由 b|= 1
完成的(按照 C 约定,这可以定义为一元 b||
,但是 ||
被分配了另一个用途)。但是我觉得这不是一个有用的位运算,而且只能自增一次。 IMO 标准增量运算符更按位。
如果您的目标实际上是“递增”布尔变量,即将其设置为真(前缀或后缀),b= b || true
是一种方法。 (没有“逻辑或赋值”b||= true
,更没有“逻辑增量”b||||
。)
这里没有“奇怪的行为”。 &
运算符,在您的情况下是按位算术运算符。这些运算符是可交换的,因此在计算结果值之前必须评估两个操作数。
如前所述,这被认为是未定义的行为,因为标准不会阻止计算+将左操作数加载到寄存器中,然后再对第二个操作数执行相同的操作,或者以相反的顺序执行此操作。
如果您使用的是 C++14 或更高版本,您可以使用 std::exchange 来实现此目的,或者使用以下方法很容易地重新实现它:
bool exchange_bool(bool &var, bool &&val) {
bool ret = var;
var = val;
return ret;
}
如果您最终要重新实现它,请考虑使用模板而不是硬编码类型。
作为对您问题的正确回答:
So my question is, how is this expression being evaluated so that it simplifies to always being true?
如果我们严格遵循标准,它不会简化,但我猜你总是使用相同的编译器,最终产生相同的输出。
旁白:你不应该“紧凑”的功能。大多数时候,这样做的代价是可读性较差。如果您是一个项目的唯一开发人员,这可能没问题,但在更大的项目中,或者如果您稍后返回您的代码,这可能是一个问题。最后但同样重要的是,“紧凑”函数更容易出错,因为它们缺乏可读性。
如果你真的想要更短的函数,你应该考虑使用子函数来处理部分函数计算。这将使您能够编写简短的函数,以快速修复或更新行为,而不会影响清晰度。
在得知不能在标准中递增布尔值后,我正在处理一些 C++ 代码。我认为递增布尔值会很有用,因为如果可以的话我喜欢压缩函数并且
unsigned char b = 0; //type doesn't really matter assuming no overflow
while (...) {
if (b++) //do something
...}
有时很有用,但不用担心整数溢出会很好。由于布尔值不能取除 0 或 1 以外的任何值,我认为这也许可行。当然,您可以在执行操作后将布尔值分配给 1,但这需要额外的代码行。
无论如何,我考虑了如何以不同的方式实现这一点,然后想到了这个。
bool b = 0;
while (...)
if (b & (b=1)) ...
即使在第一次通过时,结果总是评估为真。我想 - 当然,它只是从右到左执行按位运算符,除了当我交换顺序时,它做了同样的事情。 所以我的问题是,这个表达式是如何计算的,以便它简化为始终为真?
顺便说一句,我想做我想做的事情的方法是这样的:
bool b = 0;
while (...) if (!b && !(b=1)) //code executes every time except the first iteration
还有一种方法可以让它只执行一次。
或者只做实际可读且明显的解决方案。
bool b = 0;
while (...) {
if (b) {} else {b=1;}
}
这就是 std::exchange
的用途:
if (std::exchange(b, true)) {
// b was already true
}
如前所述,排序规则意味着 b & (b=1)
在 C 和 C++ 中都是 undefined behaviour。您不能修改变量并“在没有适当分离的情况下”从中读取(例如,作为单独的语句)。
我不明白您可以赋予“按位递增”运算符什么含义。如果我是对的,你所实现的只是设置低阶位,这是由 b|= 1
完成的(按照 C 约定,这可以定义为一元 b||
,但是 ||
被分配了另一个用途)。但是我觉得这不是一个有用的位运算,而且只能自增一次。 IMO 标准增量运算符更按位。
如果您的目标实际上是“递增”布尔变量,即将其设置为真(前缀或后缀),b= b || true
是一种方法。 (没有“逻辑或赋值”b||= true
,更没有“逻辑增量”b||||
。)
这里没有“奇怪的行为”。 &
运算符,在您的情况下是按位算术运算符。这些运算符是可交换的,因此在计算结果值之前必须评估两个操作数。
如前所述,这被认为是未定义的行为,因为标准不会阻止计算+将左操作数加载到寄存器中,然后再对第二个操作数执行相同的操作,或者以相反的顺序执行此操作。
如果您使用的是 C++14 或更高版本,您可以使用 std::exchange 来实现此目的,或者使用以下方法很容易地重新实现它:
bool exchange_bool(bool &var, bool &&val) {
bool ret = var;
var = val;
return ret;
}
如果您最终要重新实现它,请考虑使用模板而不是硬编码类型。
作为对您问题的正确回答:
So my question is, how is this expression being evaluated so that it simplifies to always being true?
如果我们严格遵循标准,它不会简化,但我猜你总是使用相同的编译器,最终产生相同的输出。
旁白:你不应该“紧凑”的功能。大多数时候,这样做的代价是可读性较差。如果您是一个项目的唯一开发人员,这可能没问题,但在更大的项目中,或者如果您稍后返回您的代码,这可能是一个问题。最后但同样重要的是,“紧凑”函数更容易出错,因为它们缺乏可读性。 如果你真的想要更短的函数,你应该考虑使用子函数来处理部分函数计算。这将使您能够编写简短的函数,以快速修复或更新行为,而不会影响清晰度。