简体   繁体   English

在遗留编译器中,逻辑否定是否已被实现为按位否定?

[英]could logical negation have been implemented as bitwise negation in legacy compilers?

I just found legacy code which tests a flag like this: 我刚刚发现遗留代码测试这样的标志:

if( some_state & SOME_FLAG )

So far, so good! 到现在为止还挺好!
But further in code, I see an improper negation 但是在代码中,我看到了一个不正当的否定

if( ! some_state & SOME_FLAG )

My understanding is that it is interpreted as (! some_state) & SOME_FLAG which is probably a bug, and gcc logically barks with -Wlogical-not-parentheses ... 我的理解是它被解释为(! some_state) & SOME_FLAG这可能是一个bug,而gcc在逻辑上咆哮着-Wlogical-not-parentheses ...

Though it could eventually have worked in the past if ever !some_state was implemented as ~some_state by some legacy compiler. 虽然它最终可能在过去有效!some_state被一些遗留编译器实现为~some_state Does anyone know if it was possibly the case? 有谁知道是否可能如此?

EDIT 编辑

sme_state is declared as int (presumably 32 bits, 2 complement on target achitecture). sme_state声明为int(可能是32位,目标sme_state上的2个补码)。
SOME_FLAG is a constant set to a single bit 0x00040000, so SOME_FLAG & 1 == 0 SOME_FLAG是一个常量设置为单个位0x00040000,因此SOME_FLAG & 1 == 0

Logical negation and bitwise negation have never been equivalent. 逻辑否定和按位否定从来都不相同。 No conforming compiler could have implemented one as the other. 没有符合标准的编译器可以实现另一个。 For example, the bitwise negation of 1 is not 0, so ~1 != !1 . 例如,1的按位取反不是0,所以~1 != !1

It is true that the expression ! some_state & SOME_FLAG 表达确实如此! some_state & SOME_FLAG ! some_state & SOME_FLAG is equivalent to (! some_state) & SOME_FLAG because logical negation has higher precedence than bitwise and. ! some_state & SOME_FLAG相当于(! some_state) & SOME_FLAG因为逻辑否定的优先级高于bitwise和。 That is indeed suspicious , but the original code is not necessarily in error. 这确实是可疑的 ,但原始代码不一定是错误的。 In any case, it is more likely that the program is buggy in this regard than that any C implementation evaluated the original expression differently than the current standard requires, even prior to standardization. 在任何情况下,在这方面,程序更有可能是错误的,因为任何C实现都会以不同于当前标准的要求评估原始表达式,甚至在标准化之前。

Since the expressions (! some_state) & SOME_FLAG and !(some_state & SOME_FLAG) will sometimes evaluate to the same value -- especially if SOME_FLAG happens to expand to 1 -- it is also possible that even though they are inequivalent, their differences do not manifest during actual execution of the program. 由于表达式(! some_state) & SOME_FLAG!(some_state & SOME_FLAG)有时会评估为相同的值 - 特别是如果SOME_FLAG恰好扩展为1 - 也有可能即使它们不相等,它们的差异也不会在实际执行程序期间显示。

While there was no standard before 1989, and thus compilers could do things as they wished, no compiler to my knowledge has ever done this; 虽然1989年之前没有标准,因此编译器可以按照自己的意愿做事,但据我所知,没有任何编译器可以做到这一点; changing the meaning of operators wouldn't be a smart call if you want people to use your compiler. 如果您希望人们使用您的编译器,更改运算符的含义将不是一个聪明的调用。

There's very little reason to write an expression like (!foo & FLAG_BAR) ; 写一个像(!foo & FLAG_BAR)这样的表达式的理由很少; the result is just !foo if FLAG_BAR is odd or always zero if it is even. 结果只是!foo如果FLAG_BAR是奇数,或者如果是偶数则总是为零。 What you've found is almost certainly just a bug. 你发现的几乎肯定只是一个bug。

It would not be possible for a legacy compiler to implement ! 遗留编译器无法实现! as bitwise negation, because such approach would produce incorrect results in situations when the value being negated is outside the {0, 0xFF...FF} set. 作为按位否定,因为当被否定的值超出{0,0xFF ... FF}集时,这种方法会产生不正确的结果。

Standard requires the result of !x to produce zero for any non-zero value of x . 标准要求的结果!x产生零对任何非零值x Hence, applying ! 因此,申请! to, say, 1 would yield 0xFF..FFFE, which is non-zero. 比方说,1会产生0xFF..FFFE,这是非零的。

The only situation when the legacy code would have worked as intended is when SOME_FLAG is set to 1 . 遗留代码按预期工作的唯一情况是SOME_FLAG设置为1

Let's start with the most interesting (and least obvious) part: gcc logically barks with -Wlogical-not-parentheses . 让我们从最有趣(也是最不明显)的部分开始:gcc逻辑上用-Wlogical-not-parentheses咆哮。 What does this mean? 这是什么意思?

C has two different operators that have similar looking characters (but different behaviour and intended for very different purposes) - the & which is a bitwise AND, and && which is a boolean AND. C有两个不同的运算符,它们具有相似的外观字符(但行为不同,用于非常不同的目的) - &是一个按位AND,而&&是一个布尔AND。 Unfortunately this led to typos, in the same way that typing = when you meant == can cause problems, so some compilers (GCC) decided to warn people about " & without parenthesis used as a condition" (even though it's perfectly legal) to reduce the risk of typos. 不幸的是,这导致了拼写错误,就像打字=当你的意思==会导致问题时,所以一些编制者(GCC)决定警告人们“ &没有括号作为条件”(即使它是完全合法的)减少错别字的风险。

Now... 现在...

You're showing code that uses & (and not showing code that uses && ). 您正在显示使用&代码(并且不显示使用&&代码)。 This implies that some_state is not a boolean and is number. 这意味着some_state不是布尔值而且是数字。 More specifically it implies that each bit in some_state may be completely independent and unrelated. 更具体地说,它意味着some_state中的每个位可以完全独立some_state相关。

For an example of this, let's pretend that we're implementing a Pacman game and need a nice compact way to store the map for each level. 举个例子,让我们假装我们正在实施一个Pacman游戏,需要一个很好的紧凑方式来存储每个级别的地图。 We decide that each tile in the map might be a wall or not, might be a collected dot or not, might be power pill or not, and might be a cherry or not. 我们认为地图中的每个瓷砖可能都是墙,可能是一个收集的点,也可能不是,但可能是一个或不是樱桃。 Someone suggests that this can be an array of bytes, like this (assuming the map is 30 tiles wide and 20 tiles high): 有人建议这可以是一个字节数组,就像这样(假设地图是30个瓷砖宽,20个瓷砖高):

#define IS_WALL         0x01
#define HAS_DOT         0x02
#define HAS_POWER_PILL  0x04
#define HAS_CHERRY      0x08

uint8_t level1_map[20][30] = { ..... };

If we want to know if a tile happens to be safe to move into (no wall) we could do this: 如果我们想知道瓷砖是否安全进入(无墙)我们可以这样做:

    if( level1_map[y][x] & IS_WALL == 0) {

For the opposite, if we want to know if a tile is a wall we could do any of these: 相反,如果我们想知道瓷砖是否是墙,我们可以做以下任何一种:

    if( level1_map[y][x] & IS_WALL != 0) {

    if( !level1_map[y][x] & IS_WALL == 0) {

    if( level1_map[y][x] & IS_WALL == IS_WALL) {

..because it makes no difference which one it is. 因为它与哪一个没什么区别。

Of course (to avoid the risk of typos) GCC might (or might not) warn about some of these. 当然(为了避免拼写错误的风险)GCC可能(或可能不会)警告其中一些。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM