逻辑求反是否可以在旧版编译器中实现为按位求反?

时间:2018-12-10 13:43:08

标签: c

我刚刚找到了测试如下标记的遗留代码:

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

4 个答案:

答案 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可能会(或可能不会)对其中一些警告。