Search code examples
c++cbitwise-and

bitwise shifts, unsigned chars


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

Solution

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