Estoy transfiriendo un código C que usa mucha manipulación de bits a Java. El código C opera bajo el supuesto de que int tiene 32 bits de ancho y char tiene 8 bits de ancho. Hay afirmaciones en él que verifican si esas suposiciones son válidas.
Ya he llegado a un acuerdo con el hecho de que tendré que usar long en lugar de unsigned int . Pero, ¿puedo usar byte de manera segura como reemplazo de unsigned char ?
Simplemente representan bytes, pero ya me encontré con este extraño incidente: ( data son un unsigned char * en C y un byte[] en Java):
/* C */ uInt32 c = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; /* Java */ long a = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]) & 0xffffffff; long b = ((data[0] & 0xff) << 24) | ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff) & 0xffffffff; Uno pensaría que una operación de cambio a la izquierda es segura. Pero debido a las extrañas reglas de promoción unaria en Java, a y b no van a ser lo mismo si algunos de los bytes en data son "negativos" ( b da el resultado correcto).
¿Qué otros "trampas" debo tener en cuenta? Realmente no quiero usar short aquí.
Puede usar un byte de forma segura para representar un valor entre 0 y 255 si se asegura de hacer AND bit a bit su valor con 255 (o 0xFF) antes de usarlo en los cálculos. Esto lo promociona a un int y garantiza que el valor promocionado esté entre 0 y 255.
De lo contrario, la promoción de enteros daría como resultado un valor int entre -128 y 127, utilizando la extensión de signo. -127 como byte (0x81 hexadecimal) se convertiría en -127 como int (0xFFFFFF81 hexadecimal).
Así que puedes hacer esto:
long a = (((data[0] & 255) << 24) | ((data[1] & 255) << 16) | ((data[2] & 255) << 8) | (data[3] & 255)) & 0xffffffff; Tenga en cuenta que el primer & 255 no es necesario aquí, ya que un paso posterior enmascara los bits adicionales de todos modos ( & 0xffffffff ). Pero probablemente sea más simple incluirlo siempre.
... ¿puedo usar
bytede forma segura como reemplazo deunsigned char?
Como has descubierto, en realidad no... No.
De acuerdo con la documentación de Oracle Java , byte es un tipo entero con signo, y aunque tiene 256 valores distintos (debido a la especificación de rango explícita "Tiene un valor mínimo de -128 y un valor máximo de 127 (inclusive)" de la documentación) hay valores que un unsigned char de C puede almacenar, que un byte de Java no puede (y viceversa).
Eso explica el problema que has experimentado. Sin embargo, el alcance del problema no se ha demostrado completamente en su implementación de bytes de 8 bits.
¿Qué otros "trampas" debo tener en cuenta?
Si bien se requiere que un byte en Java admita solo valores entre (e inclusive) -128 y 127, Cs unsigned char tiene un valor máximo ( UCHAR_MAX ) que depende de la cantidad de bits utilizados para representarlo ( CHAR_BIT ; al menos 8) . Entonces, cuando CHAR_BIT es mayor que 8, habrá valores adicionales más allá de 255 que se pueden almacenar unsigned char .
En resumen, en el mundo de Java, un byte realmente debería llamarse octet (un grupo de ocho bits), mientras que en C un byte ( char , signed char , unsigned char ) es un grupo de al menos (posiblemente más de) ocho bits .
No. No son equivalentes. Tampoco creo que encuentre un tipo equivalente en Java; todos son más bien de ancho fijo . Sin embargo, podría usar byte en Java como un equivalente para int8_t en C (excepto que no se requiere que int8_t exista en C a menos que CHAR_BIT == 8 ).
En cuanto a las trampas, también hay algunas en su código C. Suponiendo data[0] es un unsigned char , data[0] << 24 es un comportamiento indefinido en cualquier sistema para el que INT_MAX == 32767 .