【问题标题】:Explain Bit Test macro in C++解释 C++ 中的位测试宏
【发布时间】:2015-12-01 18:45:46
【问题描述】:

我试图弄清楚这段代码是如何工作的,但我无法得到一个答案。

#define testbit(x, y)  ( ( ((const char*) & (x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

我是指针新手,所以如果你能找到一种用简体英语解释的方法,我将不胜感激。

它属于在https://code.google.com/p/xplugins/source/browse/trunk/Xsaitekpanels/SwitchPanel.cpp?r=38line=19 找到的 X-Plane 插件的一段代码

【问题讨论】:

  • 为什么投反对票?因为它提到了一个宏??这是一个很好的问题。只需将移位和位屏蔽操作分解为单独的步骤。我现在只是懒得回答......

标签: c++ testing macros c-preprocessor bit


【解决方案1】:

宏测试xy-th 位的值。您不能直接寻址位,因此代码首先将 x 视为一个字节数组(const char* 转换)。

然后它会查找该位所在的字节。一个字节有 8 位,所以它除以 8。追逐性能,而不是简单地除以 8,代码使用了右移 3 位的二进制技巧。一般来说,对于未签名的xyx >> y = x/2^yx << y = x*2^y

此时您需要测试字节内的位,因此您得到y/8 的剩余部分。还有一个小技巧,使用y & 7 而不是更清晰的y % 8

使用此信息,您可以制作一个掩码,一个单位,0x80,并将其移动到位置以测试y%8-th 位。掩码与字节进行“与”运算,这里的非零结果意味着该位被设置为 1,否则为 0。

【讨论】:

  • 谢谢,节奏拳手。当它除法时,它是否移动位,将它们旋转到字节的高位,例如,0x06 >> 3 等于 0xC0 还是等于 0?或任何其他结果?
  • @user3466744 如果这个答案对你有帮助,你应该接受它:)
  • c++ 中没有旋转,从左边添加零,所以整数数学中的 0x6 >> 3 = 6/8 = 0。我在答案中添加了一些关于移位技巧的注释。
  • @RhythmicFistman 不正确,假设您正在处理一个无符号整数。有符号整数通过算术右移进行移位。
  • 我不认为这是有保证的,这就是我在答案中指定 unsigned 的原因。
【解决方案2】:

完成@RhythmicFistman 的回答

@RhythmicFistman 的回答缺少一小部分,这是轮班的最后一步。

>> (7-((y)&0x07) 步骤确保您只得到 1 或 0 的结果。使用此代码可以安全地进行如下比较:

if (testbit(varible, 6) == 1) {
    // do something
}

如果没有该步骤,testbit 将返回一个位掩码,其中第 6 位将设置为 1 或 0,而所有其他位始终设置为 0。这就是意图,但它没有在什么中实现被认为是一种便携方式,请参阅下面的警告 3

使用此代码可能出现的问题

现在为其他答案添加一些内容。其他答案没有指出这里应该提到的一些关键字,它们是strict aliasingshift arithmetic right。我的详细说明将在下面以警告的形式出现。

警告 1:Endianness

此代码假定您使用的是big endian 架构,或者只希望从字符数组中获取正确的位。

原因是,如果您将int 转换为chars(字节)的数组,您将在big endian 机器和little endian 机器上得到不同的结果。

警告 2:Strict Aliasing

该宏使用(const char*) &(x) 类型转换,旨在更改(x) 的类型,也就是别名,以便更容易获得正确的位。

这是危险的,原因在this SO answer 中有很好的解释。简短的版本是,如果您使用优化编译此代码,可能会发生奇怪的事情。

AliasingPointer Aliasing 上的维基百科页面也很有用,应该阅读。

警告 3:Shift Arithmetic Right

除此之外,此代码使用右移运算符>> 的方式可能存在潜在问题。此运算符有两种不同的行为,具体取决于它所操作的变量是 signed 还是 unsigned。只要您从不使用负数,您将是安全的,但此代码不会保护您免受该错误的影响。不过我怀疑,无论如何你不太可能犯这样的错误,所以使用它应该没问题。

另外值得一提的是,您正在使用signed char 并且正在将其正确移动。虽然这可行,但我更喜欢unsigned char,这将提高可移植性,因为当charint 的宽度相同时,它不会冒产生算术右移的风险(这在实践中几乎从未发生过,当然)。之所以有效,是因为 char 被提升为 int 以进行轮班,请参阅 this SO answer 以获得解释。

【讨论】:

    【解决方案3】:

    你看到的是一个宏,它完成了以下工作:

    (按顺序)

    • 对 y 进行位移(值:3)
    • 获取 x 的地址并选择位置 y 的字符(进入字符串 x)
    • 在选定的字符和0x80之间进行二元运算
    • 对上一个结果进行位移(值:y和0x7之间二进制运算的结果)
    • 对前一个结果进行位移(值:7 -(y 和 0x7 之间的二进制运算结果))

    嗯,这对你有帮助吗?我不这么认为!

    因为这个宏显然不合适,而且有点棘手。

    位掩码、二进制运算、二进制移位...

    如果您能更准确地解释您想了解的内容,也许我可以提供帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-11
      • 2015-10-05
      • 2015-03-07
      • 1970-01-01
      相关资源
      最近更新 更多