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:
- How does
byte = (value & mask) / shift
isolate individual bits? - Why
if(byte & 0x80)
means "If the highest bit in the byte isn't 0?" - How do these lines:
byte *= 2;
,mask /= 256;
andshift /= 256;
move bits and why is this operation significant?
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).
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).
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.
You can shift any binary value by multiplying or dividing by powers of 2, that's how binary math works.
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 AND
s 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
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