Что делает 0xff << n в Java?
Из входного потока я прочитал первые 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
Ответы
что-то вроде этого?
Да, за исключением того, что операнды подвергаются числовому преобразованию и превращаются в 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
Показанный код в example1 принимает первые четыре значения массива (который предположительно содержит то, что вы описываете как «входной поток», или его значения). Он упорядочивает их в одну переменную, предположительно (беззнаковое) целое число достаточной ширины.
Это делается для того, чтобы получить предположительно семантическое значение первых четырех значений в соответствии с протоколом, который вы не указали.
Итак, зачем делать это таким «окольным» способом, используя эти операторы?
Использование & 0xffгарантирует, что используются только младшие 8 бит.
Это избыточно, т. Е. Не нужно, в случае, если вы можете полагаться на значения в массиве, полученные из байтов и, следовательно, содержащие только значения до бита 7. Без каких-либо старших битов (значения 256, 512, ...) учитываются. Это имеет смысл, если вы не доверяете извлечению отдельных байтов из потока в массив.
Однако в java (как любезно указал Гарольд в комментарии)
& 0xffне является избыточным в Java, потому что byte является типом со знаком и, следовательно, расширяется знаком при преобразовании в int, и эти дополнительные копии знака необходимо удалить. Если бы байт был беззнаковым, это сэкономило бы много такого кода.
Использование << N, Nкратное 8, перемещает восемь «собранных» битов в позицию внутри значения результата, где они имеют правильное влияние на значение результата. Один из байтов (я намеренно не использую здесь «первый») принадлежит позиции с самым низким значением, он не сдвигается, его значение используется как есть. Один байт необходим в качестве следующего байта с более высоким значением, т. Е. Представляет собой число, кратное 256 (0x100). И так далее.
Использование |собирает правильно расположенные детали.
Зачем делать это таким сложным способом? Очевидная альтернатива - просто уничтожить все биты после бита 7, а затем прочитать партию как одно 4-байтовое целое число.
Причина в сомнении по порядку байтов. Вы можете не верить, что расположение четырех байтов в определенном порядке в памяти при чтении как одно 4-байтовое значение будет интерпретировано как значение, которое вы нацеливаете в своей среде. Это связано с тем, что в разных средах могут быть разные мнения о том, следует ли считать байт наименьшего адреса байтом, представляющим наименьшие значения или наибольшие значения.
Показанный код принудительно устанавливает выбранный байт на высокие значения, один на низкие значения, а остальные на промежуточные.
Это приведет к преобразованию значения в 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)
Это бинарные операции.
Что именно происходит?
в & 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. Осторожно, это не обязательно должна быть сумма этих чисел.