我刚刚找到了测试如下标记的遗留代码:
if( some_state & SOME_FLAG )
到目前为止,太好了!
但是在代码中,我发现否定取反
if( ! some_state & SOME_FLAG )
我的理解是,它被解释为(! some_state) & SOME_FLAG
,这可能是一个错误,并且gcc在逻辑上吠叫-Wlogical-not-parentheses
...
尽管如果某个传统编译器将!some_state
实施为~some_state
,它最终可能会在过去工作。有人知道这是否可能吗?
编辑
sme_state
被声明为int(大概32位,目标体系结构上的2补码)。
SOME_FLAG
是设置为单个0x00040000的常量,因此SOME_FLAG & 1 == 0
答案 0 :(得分:4)
逻辑取反和按位取反从来都不是等效的。没有一个合格的编译器可以实现另一个。例如,按位取反1不是0,所以~1 != !1
。
的确,表达式! some_state & SOME_FLAG
与(! some_state) & SOME_FLAG
等效,因为逻辑否定的优先级高于按位与。确实是可疑,但是原始代码不一定有错误。无论如何,即使是在标准化之前,该程序在此方面的错误也有可能比任何C实现对原始表达式的评估都不同于当前标准所要求的更高。
由于表达式(! some_state) & SOME_FLAG
和!(some_state & SOME_FLAG)
有时会得出相同的值-尤其是如果SOME_FLAG
恰好扩展为1
时,即使尽管它们是不等价的,但是它们的差异不会在程序的实际执行过程中体现出来。
答案 1 :(得分:3)
尽管1989年之前没有标准,因此编译器可以按照自己的意愿进行操作,但据我所知,还没有编译器做到过;如果您希望人们使用您的编译器,那么更改运算符的含义将不是明智的选择。
几乎没有理由编写类似(!foo & FLAG_BAR)
的表达式;如果!foo
为奇数,则结果仅为FLAG_BAR
;如果为偶数,则结果始终为零。您发现的几乎几乎肯定是一个错误。
答案 2 :(得分:3)
传统编译器不可能将!
实现为按位取反,因为在取反值超出{0,0xFF ... FF}集的情况下,这种方法会产生不正确的结果
标准要求!x
的结果为x
的任何非零值产生零。因此,将!
应用于1,将产生0xFF..FFFE,该值非零。
仅当SOME_FLAG
设置为1
时,传统代码才能按预期工作。
答案 3 :(得分:0)
让我们从最有趣(也是最不明显)的部分开始:gcc在逻辑上以-Wlogical-not-parentheses
吠叫。这是什么意思?
C有两个不同的运算符,它们具有相似的外观字符(但行为不同且用途非常不同)-&
是按位AND,而&&
是布尔AND。不幸的是,这导致了错别字,就像在您意指=
时键入==
可能会导致问题一样,因此某些编译器(GCC)决定警告人们有关“ &
,而无需使用括号一个条件”(即使完全合法)以减少错别字的风险。
现在...
您正在显示使用&
的代码(而不是显示使用&&
的代码)。这意味着some_state
不是布尔值,而是数字。更具体地说,它暗示some_state
中的每个位可能是完全独立且不相关的。
举一个例子,我们假设我们正在实现一个吃豆子游戏,并且需要一种很好的紧凑方式来存储每个关卡的地图。我们决定地图上的每个图块可能不是墙壁,可能不是收集点,可能不是药丸,也可能不是樱桃。有人建议这可以是字节数组,就像这样(假设地图宽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( level1_map[y][x] & IS_WALL == 0) {
反之,如果我们想知道瓷砖是否是墙,我们可以做以下任何事情:
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) {
..因为它没有区别。
当然(为避免发生错别字的风险),GCC可能会(或可能不会)对其中一些警告。