Неверный результат с побитовым включением ИЛИ - PullRequest
0 голосов
/ 07 мая 2018

Я не могу понять, почему инклюзивное ИЛИ возвращает неправильный результат.

char arr[] = { 0x0a, 0xc0 };
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.

В чем ошибка в этом примере? Консольное приложение MVS Community 2017.

Ответы [ 3 ]

0 голосов
/ 07 мая 2018

Я получил его для правильной работы, выполнив:

int arr[] = { 0x0a, 0xc0 };
int n{};

n = arr[0]; // I get 0x000a here.
n = n << 8; // Shift to the left and get 0x0a00 here.
n = n | arr[1];
std::cout << n << std::endl;

Произошло некоторое усечение, если вы оставили массив 'arr' как char.

0 голосов
/ 07 мая 2018

Непреднамеренное 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' };
, чтобы получить серьезные жалобы компилятора. Я думаю, эти жалобы были тесно связаны с этой проблемой.

0 голосов
/ 07 мая 2018

Вы стали жертвой целочисленной акции со знаком .

При присваивании 0xc0 второму элементу (подписанный символ по умолчанию из-за MVS) в массиве это представляется следующим образом:

arr[1] = 1100 - 0000, or in decimal -64

Когда это приведено к uint16_t, оно превращается в целое число со значением -64. Это:

n = 1111 - 1111 - 1100 - 0000 = -64  

из-за дополнения 2 к реализации целых чисел.

Таким образом:

n          = 1111 - 1111 - 1100 - 0000 
arr[1]     = 0000 - 0000 - 1010 - 0000 (after being promoted)

n | arr[1] = 1111 - 1111 -1110-0000 = 0xffc0
...