1

I cannot see the difference between y and y2

uint16_t x = 0x0F1F2;
uint8_t y = (x & 0xFF00) >> 8;
uint8_t y2 = x >> 8;
printf("%x,", y);
printf("%x,", y2);

But I see the style with a mask often. Is there ever any reason to mask with & before doing this operation?

  • 1
    The fact that you're starting with a `uint16_t` makes the mask unnecessary. The fact that you're putting the result into a `uint8_t` makes the mask unnecessary. So in your example, the mask is doubly unnecessary. People will do it anyways, because an unnecessary mask won't hurt anything, but omitting a necessary mask is a bug that could get them into all sorts of trouble. – user3386109 Feb 02 '20 at 05:09

2 Answers2

2

Typically you might mask before if some bits represent an integer stored in the value. This would probably be a hardware or network interface. So you might have the bit mask of 0xff00 defined, and a shift count, so you mask first then shift.

As for your example, y and y2 are the same, and if you compile and optimize (possibly in separate compilations), the code generated should be the same. (And will almost certainly not contain a shift operation, as it just needs to read the right byte.)

David G.
  • 595
  • 1
  • 4
  • 11
  • Could you please elaborate on `would probably be a hardware or network interface` with examples? I am interested. – aafulei Feb 02 '20 at 04:39
  • 1
    @aafulei The point is that this kind of technique is used when you need to stuff a small number into a larger field with limited space. If you can just use an `uint16_t` or `int`, why bother? So it would often be a hardware device, where I've often seen a 3 bit logarithmic value (for example chip NAU7802, REG1 and REG2). In network, consider the first byte of the IPv4 protocol: The mask 0xF0 gets the version number (4), while mask 0x0F gets the length in 4 byte units (tpically 5). – David G. Feb 02 '20 at 04:50
  • But in this situation, there's no reason to mask? You use `FF`if you want the whole value, which is the same as shifting. Also instead of shifting, what will it do instead? – spin_round_22 Feb 02 '20 at 04:53
  • 1
    @spin_round_22 Instead of shifting, a good optimizing compiler will simply read the relevant byte. For example, on x86, if `x` was stored in register `bx`, and `y` was to stored in register `dl`, it would do `MOV dl,bh` . If both where in memory, it might be `MOV al, x+1 ; MOV y,al` where x and y have been defined as data locations. – David G. Feb 02 '20 at 05:01
  • I see. In this context (extracting stuffed data) is there any realistic instance that a signed int would be used? IE if it was typical for a module to send 4 chars in a unin32t, its easy - but is there a scenario where this technique would be used with signed ints? I'm asking because I wrote a tiny utility that does all of these operations but only for unsigned types. – spin_round_22 Feb 02 '20 at 05:41
  • @spin_round_22 I could imagine using a signed value for certain *very* rare cases, where one has a small range, akin to a Unix "nice" value. (-20 to 20). In short, I wouldn't worry about signed types for your tiny utility. And hardware would usually remap the zero point anyway (-4 to 3 would become 0 to 7). – David G. Feb 02 '20 at 06:16
1

In this case the mask isn't needed. There are cases when you want to mask bits, but there is no sense in masking bits that are going to be shifted away. As long as you're using unsigned for bitwise operations, you're fine.

But since we're mentioning unsigned, keep in mind that all types smaller than int will be promoted to int for bitwise and arithmetic operations, and signed int kinda sucks. Left shifting a signed int which would lead to an overflow is undefined behavior. Right shifting will usually be implemented as an aritmetic shift (implementation dependant -- but usually it will repeat the sign bit when right-shifting), and this can surprise people at times:

uint16_t x = 0xF123;
uint32_t y = (x << 16) >> 16;   // implementation-dependant, but likely 0xFFFFF123 
uint32_t z = (x << 28);         // undefined behavior
vgru
  • 49,838
  • 16
  • 120
  • 201