bitwise shifts, unsigned chars

hatebyte picture hatebyte · Mar 7, 2014 · Viewed 10.1k times · Source

Can anyone explain verbosely what this accomplishes? Im trying to learn c and am having a hard time wrapping my head around it.

void tonet_short(uint8_t *p, unsigned short s) {
  p[0] = (s >> 8) & 0xff;
  p[1] = s & 0xff;
}

void tonet_long(uint8_t *p, unsigned long l)
{
  p[0] = (l >> 24) & 0xff;
  p[1] = (l >> 16) & 0xff;
  p[2] = (l >> 8) & 0xff;
  p[3] = l & 0xff;
}

Answer

Utkan Gezer picture Utkan Gezer · Mar 7, 2014

Verbosely, here it goes:

As a direct answer; both of them stores the bytes of a variable inside an array of bytes, from left to right. tonet_short does that for unsigned short variables, which consist of 2 bytes; and tonet_long does it for unsigned long variables, which consist of 4 bytes.

I will explain it for tonet_long, and tonet_short will just be the variation of it that you'll hopefully be able to derive yourself:

unsigned variables, when their bits are bitwise-shifted, get their bits shifted towards the determined side for determined amount of bits, and the vacated bits are made to be 0, zeros. I.e.:

unsigned char asd = 10; //which is 0000 1010 in basis 2
asd <<= 2;              //shifts the bits of asd 2 times towards left
asd;                    //it is now 0010 1000 which is 40 in basis 10

Keep in mind that this is for unsigned variables, and these may be incorrect for signed variables.

The bitwise-and & operator compares the bits of two operands on both sides, returns a 1 (true) if both are 1 (true), and 0 (false) if any or both of them are 0 (false); and it does this for each bit. Example:

unsigned char asd = 10; //0000 1010
unsigned char qwe = 6;  //0000 0110
asd & qwe;              //0000 0010 <-- this is what it evaluates to, which is 2

Now that we know the bitwise-shift and bitwise-and, let's get to the first line of the function tonet_long:

p[0] = (l >> 24) & 0xff;

Here, since l is unsigned long, the (l >> 24) will be evaluated into the first 4 * 8 - 24 = 8 bits of the variable l, which is the first byte of the l. I can visualize the process like this:

abcd efgh   ijkl mnop   qrst uvwx   yz.. ....   //letters and dots stand for
                                                //unknown zeros and ones
//shift this 24 times towards right
0000 0000   0000 0000   0000 0000   abcd efgh

Note that we do not change the l, this is just the evaluation of l >> 24, which is temporary.

Then the 0xff which is just 0000 0000 0000 0000 0000 0000 1111 1111 in hexadecimal (base 16), gets bitwise-anded with the bitwise-shifted l. It goes like this:

0000 0000   0000 0000   0000 0000   abcd efgh
&
0000 0000   0000 0000   0000 0000   1111 1111
=
0000 0000   0000 0000   0000 0000   abcd efgh

Since a & 1 will be simply dependent strictly on a, so it will be a; and same for the rest... It looks like a redundant operation for this, and it really is. It will, however, be important for the rest. This is because, for example, when you evaluate l >> 16, it looks like this:

0000 0000   0000 0000   abcd efgh   ijkl mnop

Since we want only the ijkl mnop part, we have to discard the abcd efgh, and that will be done with the aid of 0000 0000 that 0xff has on its corresponding bits.

I hope this helps, the rest happens like it does this far, so... yeah.