So I am extremely confused with using bitwise shifts used to extract the bytes from an integer. I find that if I do something like this...

int i = 512; 
i & 0xFF; // this gives me the first byte

That must mean i & 0xFF gives me the first byte that is stored in memory. In other words, i & 0xFF is the first of the four bytes i consists of stored at the lowest memory address. Next, I think that i << 8 & 0xFF will then shift everything left by 8 bits and giving me the second byte. However, it is actually the opposite and only i >> 8 & 0xFF gives me the next byte. I do not understand why this is true. To me, it makes the most sense to left shift to get the next byte so why do I need to right shift by 8 to get the proceeding byte?

  • $\begingroup$ i & 0xFF actually gives you the last byte, i.e. i % 256 $\endgroup$
    – Jakube
    Jul 20 '18 at 8:56
  • $\begingroup$ Okay so if i & 0xFF gives the last byte, then i >> 8 & 0xFF would give the second last byte? $\endgroup$ Jul 20 '18 at 9:02
  • $\begingroup$ Yes, i >> 8 is basically a division by 256. $\endgroup$
    – Jakube
    Jul 20 '18 at 9:16
  • $\begingroup$ So how does this relate to how the bytes are actually stored in memory? For example, on my machine the bytes are ordered in little endian so that means that taking i & 0xFF is getting the byte at the last memory address since it is the last byte? $\endgroup$ Jul 20 '18 at 9:20
  • $\begingroup$ It doesn't. All those operations are independent of endianness. E.g. if i = 0x12345678. Then i & 0xFF in big endian is [1, 2, 3, 4, 5, 6, 7, 8] & [0, 0, 0, 0, 0, 0, F, F] = [0, 0, 0, 0, 0, 0, 7, 8] = 0x78, and in little endian is [8, 7, 6, 5, 4, 3, 2, 1] & [F, F, 0, 0, 0, 0, 0, 0] = [8, 7, 0, 0, 0, 0, 0, 0] = 0x78]. And the same with the shift operations. I even checked the C++ standard. It defines i >> x as i / 2^x. $\endgroup$
    – Jakube
    Jul 20 '18 at 9:42

Contrary to what you think, extracing bytes by shifting and masking is completely unrelated to the storage order (both little-endian and big-endian storage schemes exist and don't influence the results).

An int variable is represented by 32 bits. The least significant byte is made of the eight least significant bits and you obtain them by

i & 0xFF
0b 111111111 0101010 01010101 000000000 => 0b 00000000 00000000 00000000 00000000 

The neighboring byte is made of the eight neighboring bits; you discard the eight least significant bits by shifting (>>), then mask out:

(i >> 8) & 0xFF
0b 11111111 10101010 01010101 000000000 => 0b 00000000 00000000 00000000 01010101 

And similarly for the remaining two bytes, with shifts of 16 and 24 (most significant byte).

Notice that I cared not to use the terms left/right and first/last, which are meaningless as they refer to some graphical representation or memory storage order. In the examples, the numbers are understood as binary numbers written with lsb on the right (and spacers for clarity), which is the most common convention.

  • $\begingroup$ I realise the point you're trying to make but I feel this would be clearer if you did use terms such as "left" and "right" but pointed out that their meaning is in terms of the ordinary way of writing binary numbers and has nothing to do with how they're actually laid out in some computer's memory. $\endgroup$ Jul 20 '18 at 15:21
  • $\begingroup$ @DavidRicherby: the terms least/most significant are more relevant. $\endgroup$ Jul 20 '18 at 15:45
  • $\begingroup$ This makes sense. Also, when I assign these numbers to say a bitset or something, will all those leading 0s be ignored and only the first 8 bits be kept? $\endgroup$ Jul 20 '18 at 17:45
  • $\begingroup$ @ZacheryKish: I represented what happens in the registers. What it becomes next depends on casts. $\endgroup$ Jul 20 '18 at 19:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.