Что делает 0xff << n в Java?

Sep 17 2020

Из входного потока я прочитал первые 4 байта и упаковал их, чтобы получить некоторую информацию, в данном случае размер / длину потока.

Для этого я использую следующий код (Пример 1), который копирую из другого проекта.

Пример 1: используется байтовый массив, где значения считываются из InputStream в массив с именем in_buf [] с длиной 4 и значениями {0,0,12,26}.

Пример 1

int size = (((in_buf[0] & 0xff) << 24) | ((in_buf[1] & 0xff) << 16) |
            ((in_buf[2] & 0xff) << 8) | (in_buf[3] & 0xff)); // result its 3098

и в результате я стал ценностью размера, приятно, но ..

Мне нужно объяснить, что здесь происходит, и я пытаюсь разделить все функции, чтобы лучше видеть, что происходит, и отлаживать, и я получаю следующие результаты

    int byte1 = ((in_buf[0] & 0xff) << 24); // result 0
    int byte2 = ((in_buf[1] & 0xff) << 16); // result 0
    int byte3 = ((in_buf[2] & 0xff) << 8);  // result 3072
    int byte4 = (in_buf[3] & 0xff);         // result 26

тогда я делаю вывод, что из примера 1 результат размера равен сумме 0 + 0 + 3072 + 26, но что именно здесь происходит (только со значениями 12 и 26)? или какая операция сделана?

Что-то вроде этого ?

0000 0000 0000 1100  //12  << 8
0000 1100 0000 0000  // result after << 8

А зачем нам использовать маску & 0xff ?

Потому что при отладке int byte3a = (in_buf [3] & 0xff) результат будет таким же, как int byte3b = in_buf [3]; итак 12 , * где значение in_buf [3] = 12; Я добавляю изображение из результатов отладки.

Что происходит или сделать это in_buf [3] & 0xff , чем-то вроде?

  0000 0000 0000 1100 (12)
& 0000 0000 1111 1111 (0xff)
  -------------------
  0000 0000 1111 0011 ?

Скриншот из Eclipse

Ответы

4 Sweeper Sep 17 2020 at 18:27

что-то вроде этого?

Да, за исключением того, что операнды подвергаются числовому преобразованию и превращаются в int, поэтому технически вы должны показывать 32 бита.

а зачем нам нужна маска & 0xff?

Это значит, что мы рассматриваем отрицательные байты как положительные ints, по сути, то, что toUnsignedIntпроисходит. Он ничего не делает для неотрицательных байтов, но для отрицательного байта, скажем -1:

1111 1111

Когда он будет преобразован в intиз-за числового продвижения, он будет расширен знаком до 32 бит. То есть знак останется отрицательным. В основном это означает, что если число отрицательное, мы добавляем 1, в противном случае - 0. Так -1становится:

1111 1111 1111 1111 1111 1111 1111 1111

Теперь, если вы воспользуетесь << 8этим, это будет:

1111 1111 1111 1111 1111 1111 0000 0000

что есть -256. Теперь посмотрим, что произойдет, если вы это сделаете & 0xffраньше << 8. Происходит числовое продвижение, и ваш байт преобразуется в 32, как и раньше, но & 0xffполучает только 8 младших битов! Итак, int теперь становится:

0000 0000 0000 0000 0000 0000 1111 1111

Вот как вы получаете исходный байт с заполнением нулями. А затем << 8делает очевидное:

0000 0000 0000 0000 1111 1111 0000 0000
1 Yunnosch Sep 17 2020 at 18:29

Показанный код в example1 принимает первые четыре значения массива (который предположительно содержит то, что вы описываете как «входной поток», или его значения). Он упорядочивает их в одну переменную, предположительно (беззнаковое) целое число достаточной ширины.

Это делается для того, чтобы получить предположительно семантическое значение первых четырех значений в соответствии с протоколом, который вы не указали.

Итак, зачем делать это таким «окольным» способом, используя эти операторы?

Использование & 0xffгарантирует, что используются только младшие 8 бит.
Это избыточно, т. Е. Не нужно, в случае, если вы можете полагаться на значения в массиве, полученные из байтов и, следовательно, содержащие только значения до бита 7. Без каких-либо старших битов (значения 256, 512, ...) учитываются. Это имеет смысл, если вы не доверяете извлечению отдельных байтов из потока в массив.
Однако в java (как любезно указал Гарольд в комментарии)

& 0xffне является избыточным в Java, потому что byte является типом со знаком и, следовательно, расширяется знаком при преобразовании в int, и эти дополнительные копии знака необходимо удалить. Если бы байт был беззнаковым, это сэкономило бы много такого кода.

Использование << N, Nкратное 8, перемещает восемь «собранных» битов в позицию внутри значения результата, где они имеют правильное влияние на значение результата. Один из байтов (я намеренно не использую здесь «первый») принадлежит позиции с самым низким значением, он не сдвигается, его значение используется как есть. Один байт необходим в качестве следующего байта с более высоким значением, т. Е. Представляет собой число, кратное 256 (0x100). И так далее.

Использование |собирает правильно расположенные детали.

Зачем делать это таким сложным способом? Очевидная альтернатива - просто уничтожить все биты после бита 7, а затем прочитать партию как одно 4-байтовое целое число.
Причина в сомнении по порядку байтов. Вы можете не верить, что расположение четырех байтов в определенном порядке в памяти при чтении как одно 4-байтовое значение будет интерпретировано как значение, которое вы нацеливаете в своей среде. Это связано с тем, что в разных средах могут быть разные мнения о том, следует ли считать байт наименьшего адреса байтом, представляющим наименьшие значения или наибольшие значения.
Показанный код принудительно устанавливает выбранный байт на высокие значения, один на низкие значения, а остальные на промежуточные.

Kayaman Sep 17 2020 at 18:28

Это приведет к преобразованию значения в 8-битный байт.

Поскольку в Java нет беззнаковых типов, байтовое значение 0xFFинтерпретируется как -132-битное int, т 0xFFFFFFFF. Е. В силу природы Java .

Битовая маска предназначена для отбрасывания этих дополнительных битов для отрицательных значений, чтобы они не перезаписывали биты при использовании побитового оператора OR |.

Ниже приведен пример со значением байта существ 255, хотя интерпретируются как -1и назначенint

  1111 1111 1111 1111 1111 1111 1111 (-1, but was originally an unsigned byte of 255)
& 0000 0000 0000 0000 0000 1111 1111 (0xff)
  ----------------------------------
  0000 0000 0000 0000 0000 1111 1111 (an int with the original value of 255)
Ecto Sep 17 2020 at 18:32

Это бинарные операции.

Что именно происходит?

в & 0xFFосновном означает, что вы берете последний байт.

Двоичное и (&) означает, что позиция результата будет 1, только если 1 находится в обоих числах в одной позиции (в противном случае - 0).

Другой пример:

  0000 0001 0000 1100 (268)
& 0000 0000 1111 1111 (0xff)
  -------------------
  0000 0000 0000 1100 (12)

Что происходит с 12?

  0000 0000 0000 1100 (12)
& 0000 0000 1111 1111 (0xff)
  -------------------
  0000 0000 0000 1100 (12)

сдвиг:

левый сдвиг просто сдвигает биты влево.

  0000 0000 0000 1100 (12)
<< 8
  -------------------
  0000 1100 0000 0000 (3072)

Что происходит с 26?

  0000 0000 0001 1010 (26)
& 0000 0000 1111 1111 (0xff)
  -------------------
  0000 0000 0001 1010 (26)

зачем нам использовать маску & 0xff?

В этом случае нет. Но если вы работали с большими числами (или отрицательными числами), может возникнуть необходимость в использовании маски. Также убедитесь, что нет лишних беспорядков.

Собираем все вместе с помощью или:

Да, это в основном дополнение, но только в этом случае. На самом деле происходит следующее: вы помещаете созданные вами байты & 0xFFрядом друг с другом, чтобы создать число. Вот почему вы сдвигаетесь умножением на 8 - каждый байт имеет собственное место размером 8.

допустим, у вас есть байты X, Y, Z, W. Затем (X<<24)|(Y<<16)|(Z<<8)|Wсоздаст ряд , построенный в двоичной системе, как следует: XYZW. Осторожно, это не обязательно должна быть сумма этих чисел.