2

我对按位运算有些熟悉,但这个函数只是让我头疼。

void binary_print(unsigned int value) {
  unsigned int mask = 0xff000000;   // Start with a mask for the highest byte.
  unsigned int shift = 256*256*256; // Start with a shift for the highest byte.
  unsigned int byte, byte_iterator, bit_iterator;

  for (byte_iterator=0; byte_iterator < 4; byte_iterator++) {
    byte = (value & mask) / shift; // Isolate each byte.
    printf(" ");

    for (bit_iterator=0; bit_iterator < 8; bit_iterator++) {
      // Print the byte's bits.
      if (byte & 0x80) // If the highest bit in the byte isn't 0,
        printf("1");   // print a 1.
      else
        printf("0");   // Otherwise, print a 0.

      byte *= 2;       // Move all the bits to the left by 1.
    }
    mask /= 256;       // Move the bits in mask right by 8.
    shift /= 256;      // Move the bits in shift right by 8.
  }
}

此函数接收函数的位标志,open()并在添加适当标签的 display_flags 函数的帮助下生成以下输出:

O_RDONLY : 0 : 00000000 00000000 00000000 00000000
O_WRONLY : 1 : 00000000 00000000 00000000 00000001
O_RDWR : 2 : 00000000 00000000 00000000 00000010
O_APPEND : 1024 : 00000000 00000000 00000100 00000000
O_TRUNC : 512 : 00000000 00000000 00000010 00000000
O_CREAT : 64 : 00000000 00000000 00000000 01000000
O_WRONLY|O_APPEND|O_CREAT : 1089 : 00000000 00000000 00000100 01000001 

我理解输出没有问题,但我不理解实际过程:

  1. 如何byte = (value & mask) / shift隔离各个位?
  2. 为什么if(byte & 0x80)意味着“如果字节中的最高位不是 0?”
  3. 这些行如何:byte *= 2;mask /= 256;移动shift /= 256;位以及为什么此操作很重要?
4

6 回答 6

11

1. 如何byte = (value & mask) / shift隔离各个位?

mask是一种位模式,始终将 8 个连续位设置为 1,其余位设置为 0(它以 开头0xff000000,然后0x00ff0000,依此类推。因此,当您对 and 进行按位与时maskvalue所有位value都将设置为 0,除了那些对应于由 . 指定的字节mask。那些保留它们的值。

shift被设置为相应的值,通过与shift那些在掩码中幸存下来的位进行除法,最终将出现在最右边的位中(参见问题 3 的答案如何工作)。

所以假设value0xDEADBEEFmask有它的初值0xff000000,也shift有它的初值256*256*256。然后value & mask0xDE000000,最后的结果是0x000000DE

在二进制中,示例是

value       = 11011110101011011011111011101111
mask        = 11111111000000000000000000000000
byte & mask = 11011110000000000000000000000000
result      = 00000000000000000000000001101111

2. 为什么if(byte & 0x80)意思是“如果字节中的最高位不是0?”

这里代码作者认为byte是一个 8 位变量。虽然它在技术上更大,但这里从未使用过更高的位。因此,当作者提到“最高位”时,请考虑右边的第 8 位(如果byte实际上只有一个字节大小,那么最高位)。

现在注意它0x8010000000二进制的。因此,当您使用 时,除“最高”(右起第 8 个)之外的byte & 0x80所有位byte都将设置为 0。byte & 0x80如果来自的最高位为零,则为零,如果来自的“最高”位为 1 byte,则大于零。byte

byte *= 2;3. 这些行: ,mask /= 256;和移动位是如何进行shift /= 256;的,为什么这个操作很重要?

乘以 2 相当于将位向左移动 1。例如考虑1001二进制的值 9。乘以 2 得到 18,它是10010二进制的。

与除以 2 类似,这是右移 1。除以 256 相当于 8 除以 2,因此除以 256 相当于右移 8 位。例如,这里使用这些操作将值mask从更改0xff0000000x00ff00000x0000ff00,最后更改为0x000000ff

全功能说明

有了这些知识,我们就可以看到完整的函数做了什么。在外部循环中,它遍历 in 中的 4 个字节value,从最左边的字节开始,到最右边的字节结束。它通过屏蔽当前字节并将其存储在byte.

然后内部循环遍历存储在byte. 它总是从右边开始查看第 8 位并相应地打印 1 或 0。然后将位向左移动,以便在第二次迭代中,从右数第 7 位现在是从右数第 8 位并将被打印,然后是下一位,依此类推,直到所有 8 位都以右到-左订单。

编写此函数的另一种方法是

for (int i = 31; i >= 0; i--) {
  if (value & (1 << i))
    printf("1");
  else
    printf("0");

  if (i % 8 == 0)
    printf(" ");
}

这将简单地value按从左到右的顺序遍历所有位。表达式从 中value (1 << i)选择所需的位value,从右起第 32 位开始(当i为 31),并以右起第 1 位结束(当i为 0 时)。

于 2013-02-16T14:26:55.657 回答
1

要记住的最重要的事情是bitwise逻辑依赖于对bits. 因此,出于所有意图和目的,按位& (and)是模 1 的乘法,而按位是模 1| (or)的加法。查看这一点的最简单方法是通过示例:

如果您有一些字节0xF0并且您想查看是否设置了最高位,您将and使用0x80. 这是发生的事情:

  11110000 = 0xF0
x 10000000 = 0x80
==========
  10000000 = 0x80

因此,如果实际未设置 in 的最高位0xF0,则结果将是0and 而不是0x80. 您可以通过制作二进制数来处理任何位位置或位位置序列。例如,0x88 = 10001000这将检查字节中的最高位以及第 4 位。

二进制的重要一点是要注意每个位置都是乘以 2。所以00000001 = 1但是然后00000010 = 2等等00000100 = 4。因此,乘以2就像左移 ( <<) 乘以 1。除以 256 是右移 ( >>) 乘以 8。这很容易通过思考 2 的幂来看出。2^8 = 256. 因此,由于每一位都是其中的一个2,因此除法256相当于右移 8(所需的指数/二进制数)。

于 2013-02-16T14:33:10.360 回答
1

1) value & mask 导致除您感兴趣的字节之外的所有字节都被清零。除以 shift 将其移动到字节 0 中(我个人会使用 >> 运算符)。

2) byte & 0x80 删除除最高位之外的所有位。0x80 是二进制的 10000000,1 位集合匹配一个字节中的最高位。结果现在的值为 0 或 10000000(十六进制 0x80)。只有设置了最高位,IF 才会为真。

3) byte *= 2 是左移 1 位。我会使用 byte <<= 1. 似乎更明显。

mask /= 256 是右移 8 位。我会使用掩码 >>= 8。同上

如果您使用 2 的幂,则除法和倍数可以用作移位运算符。对我来说,使用移位运算符似乎更明显。

顺序很重要,以正确顺序获取值。

于 2013-02-16T14:33:51.990 回答
0

您可以通过乘以或除以 2 的幂来移动任何二进制值,这就是二进制数学的工作原理。

于 2013-02-16T14:24:06.197 回答
0

好吧,听起来您的困难在于了解按位运算与算术的关系。

  • 首先,乘以 2 与将二进制左移 1 步相同。
  • 其次,如果你多次这样做,你就会向左移动几个步骤。
  • 第三,如果你除以 2,你就向右移动了一步。

所有这些操作的更好表示法是使用“真正的”移位运算符:

(value & mask) / (256*256*256)

最好写成

(value & mask) >> (3*8)

这有帮助吗?

我曾经喜欢考虑使用“DIV”和“MOD”将一个数字分成两部分 - 其中N DIV 256是丢弃余数的整数除法 - 所以这有效地向右移动了 8 位,丢弃了最低字节。相反的是N MOD 256它只取余数。这实际上AND是 255,并且只留下最低字节。根据 aDIVMOD结果,您可以重建原始数字:

LO = X & 255;   // equivalent to (byte)X if X is unsigned
HI = X >> 8 ;   // equivalent to (X / 256) in this case
original = LO | (HI << 8) 
 // equivalent to LO + (HI * 256), in this case
于 2013-02-16T14:26:51.443 回答
0

mask关闭除第一个字节中的那些以外的所有打开的位,例如

  0110 0000 0000 0000 0000 0000 0000 0110 0000
& 1111 1111 0000 0000 0000 0000 0000 0000 0000   
= 0110 0000 0000 0000 0000 0000 0000 0000 0000  

因为1 & 0 or 0 & 1 or 0 & 0 == 0 and 1 & 1 == 0

除以 2 将所有位右移,乘以 2 将所有位左移。

0x80 == 1000 0000因此&,使用此值会关闭除第一位之外的所有内容。

如果设置了第一位,则结果值 > 0,因此对应于布尔真值,如果不是,则为零并对应于假。

于 2013-02-16T14:29:11.847 回答