Непреднамеренное 0xff
вызвано расширением знакового бита из 0xc0
.
0xc0 = 0b11000000
Следовательно, устанавливается самый верхний бит, который означает знак для char
(как signed char
).
Обратите внимание, что все арифметические и побитовые операции в C ++ работают как минимум с int
(или unsigned int
). Меньшие типы повышаются до и затем обрезаются.
Обратите внимание, что char
может быть подписанным или неподписанным. Это зависит от реализации компилятора. Очевидно, это подписано в случае ОП. Чтобы предотвратить непреднамеренное расширение знака, аргумент должен стать беззнаковым (достаточно рано).
Демонстрация:
#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;
}
Сессия:
g++ -std=c++11 -O2 -Wall -pthread main.cpp && ./a.out
n (wrong): ffc0
n (right): ac0
Life демо на coliru
Примечание:
Мне пришлось изменить
char arr[] = { 0x0a, 0xc0 };
на
char arr[] = { '\x0a', '\xc0' };
, чтобы получить серьезные жалобы компилятора. Я думаю, эти жалобы были тесно связаны с этой проблемой.