Is left shifted short that is going to be stored in an int is UNDEFINED?

Viewed 79

I was reading a book that implement a do it yourself DNS message reader and it tries to see if a particular field is set true. One piece of code the book uses I can't understand well.

const int qdcount = (msg[10] << 8) + msg[11];

msg is ==> char type (i.e 8 bits)

qdcount ==> is supposed to be a field of 16 bits contains number of DNS queries ( made of 2 fields together msg[10] and msg[11])

so how does this code work (if msg[10] = 01001 0001 for example) left shifting it by 8 is supposed to result (1000 0000) i.e UB, then any calculations done will result in a wrong answer. Suppose msg[11] = 0010 1111. result of calculation is 1000 0000 + 0010 1111 right?. so how this line of code works exactly.

2 Answers

There are at least 2 independent considerations.

  • char is signed or unsigned?

  • int 16 bit or wider?

    (msg[10] << 8) + msg[11];
    

Most of the concern depends on:

The result of E1 << E2 is E1 left-shifted E2 bit positions ...
If E1 has a signed type and nonnegative value, and E1 *2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

char is signed, int any size

msg[10] is promoted to int and shifted left 8. This is fine when the value msg[10] is positive and undefined behavior when negative as left shifting a negative value is UB.

char is unsigned, int wider than 16 bit

msg[10] is promoted to int and shifted left 8. This is fine, no problems for all msg[10] values.

char is unsigned, int is 16 bit

msg[10] is promoted to int and shifted left 8. This is fine when msg[10] < 128, else is UB to shift into the sign place - positive value not representable.


Best to use unsigned types when shifting.

// char msg[100];
// const int qdcount = (msg[10] << 8) + msg[11];

char unsigned msg[100];
const unsigned qdcount = ((unsigned) msg[10] << 8) + msg[11];

from the C11 – ISO/IEC 9899:2011 draft paragraph 6.5.7.4 version linked here.

[...] E1 << E2 [...]
If E1 has a signed type and nonnegative value, and E1 × 2^E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

So yes. left shifting a signed value may result in undefined behavior.

Another thing in your code, that actually causes your confusion is:

The integer promotions are performed on each of the operands.

That means that before shifting your operands are expanded to int. so there is no way that shifting by 8 does lead to an overflow by shifting bits into the sign bit (assuming sizeof(int)>16).

Related