Bitwise Logic in C

拥有回忆 提交于 2019-12-08 01:28:16

问题


I have some familiarity with bitwise operations, but this function just went over my head.

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.
  }
}

This function receives bit flags for the open() function and produces the following output with the help of a display_flags function that adds the appropriate labels:

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 

I have no problem understanding the output, but I don't understand the actual process:

  1. How does byte = (value & mask) / shift isolate individual bits?
  2. Why if(byte & 0x80) means "If the highest bit in the byte isn't 0?"
  3. How do these lines: byte *= 2; , mask /= 256; and shift /= 256; move bits and why is this operation significant?

回答1:


1. How does byte = (value & mask) / shift isolate individual bits?

mask is a bit pattern that always has 8 consecutive bits set to 1, the rest to 0 (it starts with 0xff000000, then 0x00ff0000, and so on. So when you take the bitwise and of mask and value, all bits from value will be set to 0, except those that correspond to the byte that is specified by mask. Those keep their value.

shift is set to the corresponding value that by the division with shift exactly those bits that survived the masking will end up in the rightmost bits (see answer to question 3 how that works).

So assume value is 0xDEADBEEF, mask has its initial value 0xff000000, and shift has its initial value 256*256*256. Then value & mask is 0xDE000000, and the final result is 0x000000DE.

In binary the example would be

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

2. Why if(byte & 0x80) means "If the highest bit in the byte isn't 0?"

Here the code author thinks of byte being an 8-bit variable. Although it is technically larger, the higher bits are never used here. So when the author refers to the "highest bit", think of the 8th bit from the right (the highest bit that would be there if byte was actually only one byte in size).

Now note that 0x80 is 10000000 in binary. So when you take byte & 0x80, all bits from byte will be set to 0, except the "highest" (8th from right) one. So byte & 0x80 is zero, if the highest bit from byte is zero, and greater than zero, if the "highest" bit from byte is 1.

3. How do these lines: byte *= 2;, mask /= 256; and shift /= 256; move bits and why is this operation significant?

Multiplication with 2 is equivalent to a shift of bits to the left by 1. Consider for example the value 9, which is 1001 in binary. A multiplication by 2 gives 18, which is 10010 in binary.

Similar for a division by 2, this is a shift to the right by 1. Division by 256 is equivalent to 8 divisions by 2, so dividing by 256 is equivalent to a right shift by 8 bits. These operations are used here for example to change the value mask from 0xff000000 to 0x00ff0000, 0x0000ff00, and finally to 0x000000ff.

Description of full function

With this knowledge, we can see what the full function does. In the outer loop it loops over the 4 bytes that are in value, starting with the left-most one and ending with the right-most one. It does so by masking out the current byte and stores it in byte.

The inner loop then goes over the 8 bits that are stored in byte. It always looks at 8th bit from the right and prints 1 or 0 accordingly. Then it shifts the bits to the left, so that in the second iteration the bit that was the 7th from right is now the 8th from right and will be printed, and then next one etc. until all 8 bits are printed in right-to-left order.

An alternative way to write this function would be

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

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

This would simply go through all bits from value in left-to-right order. The expression value (1 << i) selects the desired bit from value, starting with the 32th from right (when i is 31), and ending with the 1st from right (when i is 0).




回答2:


The most important thing to remember is that bitwise logic relies on performing operations on bits. So for all intents and purposes, bitwise & (and) is multiplication modulo 1 and bitwise | (or) is addition modulo 1. The easiest way to see this is by example:

If you have some byte 0xF0 and you want to see if the highest bit is set you will and it with 0x80. Here is what happens:

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

Thus, if the highest bit in 0xF0 was not actually set, the result would be 0 and instead of 0x80. You can do this with any bit position or sequence of bit positions by crafting a binary number. For instance, 0x88 = 10001000 this will check the highest bit in the byte as well as the 4th bit.

The important thing with binary is to notice that each position is a multiplication by two. So 00000001 = 1 but then 00000010 = 2 and 00000100 = 4 and so on. So a multiplication by 2 is like a left shift (<<) by one. A division by 256 is a right shift (>>) by 8. This is most easily seen by thinking in powers of two. 2^8 = 256. So, since each bit is one of those 2's, the division of 256 is equivalent to shifting right by 8 (the exponent/number of twos needed).




回答3:


1) value & mask causes all the bytes to be zeroed out except the one you are interested in. dividing that by shift moves it into byte 0 (personally I would have used the >> operator).

2) byte & 0x80 removes all the bits except the highest one. 0x80 is 10000000 in binary, the 1 bit set matches the highest bit in a byte. the results will have a value of 0 or 10000000 (in hex 0x80) now. the IF will be true only if the top bit was set.

3) byte *= 2 is a left shift by 1 bit. I would have used byte <<= 1. seems more obvious.

mask /= 256 is a right shift by 8 bits. I would used mask >>= 8. ditto

divide and multiple can be used a shift operators if you use powers of 2. It seems more obvious to me to use the shift operators.

the order matters to get the values out in the correct order.




回答4:


You can shift any binary value by multiplying or dividing by powers of 2, that's how binary math works.




回答5:


Well it sounds like your difficulty is seeing how bitwise operations relate to arithmetic.

  • Firstly, multiplying by 2 is the same as shifting binary 1 step to the left.
  • secondly, if you do this several times, you shift several steps to the left.
  • Thridly, if you divide by 2, you are shifting one step to the right.

A better notation for all these operations would be using the 'real' shift operators:

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

is better written as

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

Does that help?

I used to like thinking of breaking up a number into two parts using "DIV" and "MOD" - where N DIV 256 is integer division that discard a remainder - so this effectively shifts right by 8 bits, discarding the lowest byte. And the opposite is N MOD 256, which just takes the remainder. That effectively ANDs by 255, and leaves only the lowest byte. From a DIV and MOD result, you can reconstruct your original number:

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



回答6:


mask turns off all bits switched on apart from those in the first byte, e.g.

  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  

because 1 & 0 or 0 & 1 or 0 & 0 == 0 and 1 & 1 == 0

Dividing by 2 shifts all bits right, multiplying by 2 shifts them all left.

0x80 == 1000 0000 So & with this value turns off everything except the first bit.

If the first bit is set, the resulting value is > 0 and therefore corresponds to a boolean true value, if not it's zero and corresponds to false.



来源:https://stackoverflow.com/questions/14911216/bitwise-logic-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!