The unintended 0xff
is caused by sign bit extension of 0xc0
.
0xc0 = 0b11000000
Hence, the uppermost bit is set which means sign for char
(as signed char
).
Please, note that all arithmetic and bitwise operations in C++ work with at least int
(or unsigned int
). Smaller types are promoted before and clipped afterwards.
Please, note also that char
may be signed or unsigned. That's compiler implementation dependent. Obviously, it's signed in the case of OP. To prevent the unintended sign extension, the argument has to become unsigned (early enough).
Demonstration:
#include <iostream>
int main()
{
char arr[] = { '\x0a', '\xc0' };
uint16_t n{};
n = arr[0]; // I get 0x000a here.
n = n << 8; // Shift to the left and get 0x0a00 here.
n = n | arr[1]; // But now the n value is 0xffc0 instead of 0x0ac0.
std::cout << std::hex << "n (wrong): " << n << std::endl;
n = arr[0]; // I get 0x000a here.
n = n << 8; // Shift to the left and get 0x0a00 here.
n = n | (unsigned char)arr[1]; // (unsigned char) prevents sign extension
std::cout << std::hex << "n (right): " << n << std::endl;
return 0;
}
Session:
g++ -std=c++11 -O2 -Wall -pthread main.cpp && ./a.out
n (wrong): ffc0
n (right): ac0
Life demo on coliru
Note:
I had to change
char arr[] = { 0x0a, 0xc0 };
to
char arr[] = { '\x0a', '\xc0' };
to come around serious compiler complaints. I guess, these complaints where strongly related to this issue.