How exactly do the following lines work if pData = "abc"
?
pDes[1] = ( pData[0] & 0x1c ) >> 2;
pDes[0] = ( pData[0] << 6 ) | ( pData[1] & 0x3f );
How exactly do the following lines work if pData = "abc"
?
pDes[1] = ( pData[0] & 0x1c ) >> 2;
pDes[0] = ( pData[0] << 6 ) | ( pData[1] & 0x3f );
C++ will treat a character as a number according to it's encoding. So, assuming ASCII, 'a' is 97 (which has a bit pattern of 0110_0001) and 'b' is 98 (bit pattern 0110_0010).
Once you think of them as numbers, bit operations on characters should be a bit clearer.
Okay, assuming ASCII which is by no means guaranteed, pData[0]
is 'a'
(0x61
) and pData[1]
is 'b'
(0x62
):
pDes[1]:
pData[0] 0110 0001
&0x1c 0001 1100
---- ----
0000 0000
>>2 0000 0000 0x00
pDes[0]:
pData[0] 0110 0001
<< 6 01 1000 0100 0000 (interim value *a)
pData[1] 0110 0010
&0x3f 0011 1111
-- ---- ---- ----
0010 0010
|(*a) 01 1000 0100 0000
-- ---- ---- ----
01 1000 0110 0010 0x1862
How it works:
<< N
simply means shift the bits N
spaces to the left, >> N
is the same but shifting to the right.
The &
(and
) operation will set each bit of the result to 1 if and only if the corresponding bit in both inputs is 1.
The |
(or
) operations sets each bit of the result to 1 if one or more of the corresponding bit in both inputs is 1.
Note that the 0x1862 will be truncated to fit into pDes[0]
if it's type is not wide enough.
The folowing C program shows this in action:
#include <stdio.h>
int main(void) {
char *pData = "abc";
int pDes[2];
pDes[1] = ( pData[0] & 0x1c ) >> 2;
pDes[0] = ( pData[0] << 6 ) | ( pData[1] & 0x3f );
printf ("%08x %08x\n", pDes[0], pDes[1]);
return 0;
}
It outputs:
00001862 00000000
and, when you change pDes
to a char array, you get:
00000062 00000000
&
is not logical AND - it is bit-wise AND.
a
is 0x61, thus pData[0] & 0x1c
gives
0x61 0110 0001
0x1c 0001 1100
--------------
0000 0000
>> 2
shifts this to right by two positions - value doesn't change as all bits are zero.
pData[0] << 6
left shifts 0x61
by 6 bits to give 01000000
or 0x40
pData[1] & 0x3f
0x62 0110 0010
0x3f 0011 1111
--------------
0x22 0010 0010
Thus it comes down to 0x40 | 0x22
- again |
is not logical OR, it is bit-wise.
0x40 0100 0000
0x22 0010 0010
--------------
0x62 0110 0010
The results will be different if pDes
is not a char array. Left shifting 0x61
would give you 0001 1000 0100 0000
or 0x1840
- (in case pDes
is a char array, the left parts are not in the picture).
0x1840 0001 1000 0100 0000
0x0022 0000 0000 0010 0010
--------------------------
0x1862 0001 1000 0110 0010
pDes[0]
would end up as 0x1862
or decimal 6242
.
In C, all characters are also integers. That means "abc"
is equivalent to (char[]){0x61, 0x62, 0x63, 0}
.
The &
is not the logical AND operator (&&
). It is the bitwise AND, which computes the AND at bit-level, e.g.
'k' = 0x6b -> 0 1 1 0 1 0 1 1
0x1c -> 0 0 0 1 1 1 0 0 (&
———————————————————
8 <- 0 0 0 0 1 0 0 0
The main purpose of & 0x1c
here is to extract bits #2 ~ #4 from pData[0]
. The >> 2
afterwards remove the extra zeros at the end.
Similarly, the & 0x3f
is to extract bits #0 ~ #5 from pData[1]
.
The << 6
pushes 6 zeros at the least significant end of the bits. Assuming pDes[0]
is also a char
, the most significant 6 bits will be discarded:
'k' = 0x6b -> 0 1 1 0 1 0 1 1
<< 6 = 0 1 1 0 1 0 1 1 0 0 0 0 0 0
xxxxxxxxxxx—————————————————
0xc0 <- 1 1 0 0 0 0 0 0
In terms of bits, if
pData[1] pData[0]
pData -> b7 b6 b5 b4 b3 b2 b1 b0 a7 a6 a5 a4 a3 a2 a1 a0
then
pDes -> 0 0 0 0 0 a4 a3 a2 a1 a0 b5 b4 b3 b2 b1 b0
pDes[1] pDes[0]
This looks like an operation to pack three values into a 6-5-5 bit structure.