¿Qué hace 0xff << n en Java?
Desde un Inputstream, leí los primeros 4 Bytes y los empaqueté para obtener alguna información, en este caso el tamaño / longitud del Stream.
Para eso uso el código siguiente (Ejemplo 1) que copio de otro proyecto
Ejemplo 1: utiliza una matriz de bytes, donde los valores se leen de InputStream en la matriz denominada in_buf [] con longitud 4 y cuyos valores son {0,0,12,26}.
Ejemplo 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
y como resultado me convierto en el valor del tamaño, bonito pero ...
Necesito explicar lo que sucede aquí, y trato de dividir todas las funciones para ver mejor lo que sucede y depurar, y sigo los resultados.
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
entonces deduzco que del Ejemplo 1 el resultado del tamaño es la suma de 0 + 0 + 3072 + 26 pero ¿qué sucede exactamente (solo con el valor 12 y 26) aquí? o que operación se hace?
Algo como esto ?
0000 0000 0000 1100 //12 << 8
0000 1100 0000 0000 // result after << 8
¿Y por qué necesitamos usar la máscara y 0xff ?
porque cuando depuras int byte3a = (in_buf [3] & 0xff) el resultado es el mismo que int byte3b = in_buf [3]; entonces 12 , * donde valor de in_buf [3] = 12; Agrego una imagen de mis resultados de depuración.
¿Qué sucede o hacer esto in_buf [3] & 0xff , algo como?
0000 0000 0000 1100 (12)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 1111 0011 ?
Captura de pantalla de Eclipse
Respuestas
algo como esto?
Sí, excepto que los operandos experimentan una promoción numérica y se convierten en an int, por lo que técnicamente debería mostrar 32 bits.
y por qué necesitamos usar la máscara
& 0xff?
Esto es para que tratemos los bytes negativos como positivos ints, esencialmente lo que toUnsignedIntestá haciendo. No hace nada para bytes no negativos, pero para un byte negativo, diga -1:
1111 1111
Cuando eso se convierta intdebido a la promoción numérica, se extenderá el signo a 32 bits. Es decir, el signo permanecerá negativo. Básicamente, esto significa que si el número es negativo, rellenamos 1, de lo contrario, rellenamos 0. Entonces se -1convierte en:
1111 1111 1111 1111 1111 1111 1111 1111
Ahora, si usa el << 8en eso, será:
1111 1111 1111 1111 1111 1111 0000 0000
que es -256. Ahora veamos qué pasa si lo haces & 0xffantes << 8. La promoción numérica ocurre y convierte su byte a 32 como antes, ¡pero & 0xffobtiene solo los 8 bits menos significativos! Entonces el int ahora se convierte en:
0000 0000 0000 0000 0000 0000 1111 1111
Así es como obtienes tu byte original, rellenando con ceros. Y luego << 8hace lo obvio:
0000 0000 0000 0000 1111 1111 0000 0000
El código mostrado en example1 toma los primeros cuatro valores de la matriz (que presumiblemente contiene lo que usted describe como "el flujo de entrada", o sus valores). Los organiza en una sola variable, presumiblemente un entero (sin signo) de suficiente ancho.
Lo hace para obtener lo que presumiblemente es el significado semántico de los primeros cuatro valores, de acuerdo con un protocolo que no especificó.
Entonces, ¿por qué hacerlo de esa manera "indirecta", usando esos operadores?
El uso de & 0xffasegura que solo se utilicen los 8 bits inferiores.
Esto es redundante, es decir, innecesario, en caso de que pueda confiar en los valores de la matriz que provienen de bytes y, por lo tanto, solo contienen valores hasta el bit 7. Sin bits superiores (con valor de 256, 512, ...) se tienen en cuenta. Esto tiene sentido si no confía en la recuperación de bytes individuales de la secuencia en la matriz.
Sin embargo en Java (como Harold señaló amablemente en un comentario)
& 0xffno es redundante en Java porque byte es un tipo firmado y, por lo tanto, el signo se extiende cuando se convierte a int, y esas copias adicionales del signo deben eliminarse. Si el byte no estuviera firmado, se ahorraría mucho código como este.
El uso de << N, Nsiendo múltiplos de 8, mueve los ocho bits "recolectados" a la posición dentro del valor del resultado, donde tienen la influencia correcta sobre el valor del resultado. Uno de los bytes (intencionalmente no uso "primero" aquí) pertenece a la posición de menor valor, no se cambia, su valor se usa como está. Se necesita un byte como el siguiente byte de mayor valor, es decir, que representa múltiplos de 256 (0x100). Y así.
El uso de |ensambla las piezas correctamente ubicadas.
Ahora, ¿por qué hacer eso de esta manera tan complicada? La alternativa obvia es simplemente eliminar todos los bits más allá del bit 7 y luego leer el lote como un entero de 4 bytes.
La razón es la duda sobre la endiabilidad. Es posible que no confíe en que organizar cuatro bytes en un cierto orden en la memoria, si se lee como un valor de 4 bytes, se interpretará como el valor objetivo en su entorno. Esto se debe a que diferentes entornos pueden tener opiniones diferentes sobre si el byte en la dirección más baja debe considerarse el que representa los valores más bajos o los valores más altos.
El código mostrado fuerza un byte seleccionado a los valores altos, uno a los valores bajos y los otros en el medio.
Forzará el valor en un byte de 8 bits.
Dado que Java no tiene tipos sin firmar, un valor de byte de 0xFFse interpreta como -1y debido a la naturaleza de Java, se promoverá a un int de 32 bits, es decir 0xFFFFFFFF.
La máscara de bits está destinada a descartar esos bits adicionales por valores negativos para que no sobrescriban los bits cuando se usa el operador OR bit a bit |.
Aquí hay un ejemplo con el valor de byte 255, aunque se interpreta -1y se promueve aint
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)
Estas son operaciones binarias.
¿Qué sucede exactamente?
las & 0xFFsignifica básicamente, se toma el último byte.
Binario y (&) significa que en la posición del resultado será 1 solo si 1 está en ambos números en la misma posición (0 en caso contrario).
Hay otro ejemplo:
0000 0001 0000 1100 (268)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 0000 1100 (12)
¿Qué pasa con 12?
0000 0000 0000 1100 (12)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 0000 1100 (12)
cambio:
el desplazamiento a la izquierda solo empuja los bits hacia la izquierda.
0000 0000 0000 1100 (12)
<< 8
-------------------
0000 1100 0000 0000 (3072)
¿Qué pasa con 26?
0000 0000 0001 1010 (26)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 0001 1010 (26)
¿Por qué necesitamos usar la máscara y 0xff?
En este caso, no es así. Pero si trabajó con números más grandes (o números negativos), puede ser necesario utilizar la máscara. Además, asegúrese de que no haya partes extra desordenadas.
Poniéndolo todo junto con o:
Sí, es básicamente una adición, pero solo en este caso. Lo que realmente sucede es que colocas los bytes que creaste & 0xFFuno al lado del otro para crear un número. Esa es la razón por la que está cambiando por multiplicaciones de 8: cada byte tiene su propio lugar de tamaño 8.
digamos que tiene bytes X, Y, Z, W. Entonces (X<<24)|(Y<<16)|(Z<<8)|Wcreará número construido en binario como sigue: XYZW. Cuidado, no tiene que ser la suma de estos números.