C移植到Java中,byte[]与其他数据类型的转换

最近在把C代码移植到Java(Android)中,C中一般的数据都会使用byte[](unsigned char)来存储,且位操作特别频繁。我要把这些数据转换成在Java存储,或把byte转换成本语言的其他类型,掉进不少坑。这里,总结下这段时间我遇到的转换问题。

一、C与Java的变量类型区别

1、变量类型及所占字节数

变量类型 C中字节数 Java中字节数
char 1 2
byte 【无】 1
short 2 2
int 4 4
long 4 8

C在不同的机器所占字节数不同,但一般的机器都是这样,不同的可用sizeof(int)来获取。
C中没有byte类型,但一般都会用typedef来定义一个:typedef unsigned char BYTE;

2、变量类型有无符号

C中可以定义有符号的变量和无符号的变量类型,而Java中只有带符号的变量。无符号表示的正数范围要大一个等级。
如:signed char范围是-128~127,unsigned char的范围是0~255

二、Java中注意事项

以下基本都是按照C的操作习惯类:所有的类型都是跟C一样,且当做无符号处理;常用十六进制表示数据。

1、byte、short的位操作都是先转换成int类型,再进行位操作。

因为Java中没有unsigned,所以正负数转换完全不一样。如

byte a = 0x12; 
byte b = (byte)0x81; 
...位操作...

a的最高位是0,所以是正数,直接转换成十进制即可,为18。
b的最高位是1,所以是负数。用常量值赋值,常量默认是int型,若没超过-128~127(0x7F)的范围,可直接赋值,否则必须强转。

计算机中都是用补码来存储数据,根据正数补码为原码,负数补码为去符号位后,原码取反加1。已知补码,正数的原码就是去符号位后,补码减1再取反。

补码      1000 0001
去符号     -000 0001
减1      -000 0000
取反      -111 1111
----------------------
十进制 -127

so,
a转换成int后是:0x00000012
b转成成int后是:0xFFFFFF81
转换后,如果把前面的0去掉后,正数表示还是一样,但负数前面的不再是0了,而全是F(1111)了。

我要说明的就是这点:在位操作时,首先会先转成int类型,正数被转换后前面的字节全填0,负数则填1。
谨记这点,在操作时就知道如何操作了

2、byte与int的互转

byte转int,由上面可知,如果是负数,则会出现问题。所以应该把需要的最低字节提取出来。
int转byte,直接强转即可,强转的操作相当于直接截取最低字节。这没什么意义,主要用来为下面的int转byte[]打基础。

private static int byte2Int(byte b) {
        return b & 0xFF;
}

public static byte int2Byte(int num) {
        return (byte) num;
}

3、带符号右移>>和无符号右移>>>

对于正数,两种位操作都是一样。
但对于负数来说就不一样了,带符号右移>>后最高位补1,无符号右移>>>最高位补0。
目前我用的最多的地方就是取位和整除2^n。

【1】取位
把变量上固定位的值取出。如取出a和b的高4位:

private static void testGetHighBits() {
    byte a = 0x12;
    byte b = (byte) 0x81;
    byte highA, highB;

    highA = (byte) (a >> 4);
    highB = (byte) (b >> 4);

    System.out.printf("highA=%X, highB=%X", highA, highB);
}

打印的结果是:highA=1, highB=F8
我们想要的结果应该是:highA=1, highB=8
肿么办?就用我们的>>>吧:highB = (byte) (b >>> 4);
结果还是一样。。。

想想上面第一点说的。highB = (byte) (b >>> 4);的执行流程是这样的:

b转换为int1111  1111  1111  1111  1111  1111  1000  0001
>>>40000  1111  1111  1111  1111  1111  1111  1000
强转成byte1111  1000

所以最后结果是F8。

取位,我们应该按需取:把值和需要的位相与,再移位,或先移位再与。
把需要的位取出,就是与1相与,不是吗?如
取低4位,直接&0x0F。
取高4位,&0xF0,再>>4,或先>>4,再&0x0F
取中间4位,&0x3C,再>>4,或先>>4,再&0x0F

上面的代码应该改为:

private static void testGetHighBits() {
    byte a = 0x12;
    byte b = (byte) 0x81;
    byte highA, highB;

    highA = (byte) ((a & 0xF0) >> 4);
    highB = (byte) ((b & 0xF0) >> 4);

    System.out.printf("highA=%X, highB=%X", highA, highB);
}

highA = (byte) ((a >> 4) & 0x0F);
highB = (byte) ((b >> 4) & 0x0F);

把>>替换成>>>也都可以的。注意运算符的优先级(口诀:单目乘除为关系,逻辑三目后赋值。参考http://lasombra.iteye.com/blog/991662

【2】整除2^n

我们知道<<n,相当于乘2^n;>>n相当于除2^n。(<<与MarkDown语法冲突,只能嵌套在这代码块中)

这>>n也可以用>>>n代替,那>>与>>>到底在什么时候要区分用?
目前我用到的地方就是在无符号int中。如,用int类型的变量代表0~2^32-1,并求整除4后的结果。

private static void testBitsRightMove() {
    int a = 0x12C; // 实际值:300,也是代表值
    int b = 0x80000089; // 实际值:-2147483511,但此代表:2147483785
    int tmpA, tmpB;

    System.out.printf("a=%d, 0x%X; \t b=%d, 0x%X\n", a, a, b&0xFFFFFFFFL, b);

    tmpA = a >> 2;
    tmpB = b >>> 2;

    System.out.printf("tmpA=%d; \t tmpB=%d", tmpA, tmpB);
}

打印结果:

a=300, 0x12C;    b=2147483785, 0x80000089
tmpA=75;     tmpB=536870946

上面的>>都可以替换成>>>,但>>>就不能被>>替换。在无符号的操作中,如果实际值是负数,必须使用>>>,为了保险起见,建议都用>>>。

4、如何用byte表示无符号,0~255的范围?

上面已经讲了int表示无符号的,同理的,byte也可以表示无符号的。但这仅是存储用,实际操作时必须放大范围来操作。

如有一个大小是256的byte类型数组,现在给数组赋值,元素的值为其对应的数组脚标值,并求偶数和。

private static void testUnsignedByte() {
    byte[] arr = new byte[256];
    int i;
    int sum = 0;

    // 赋值
    for (i = 0; i < 256; i++) {
        arr[i] = (byte) i;
    }

    // 打印元素,并求和
    for (i = 0; i < 256; i++) {
        int e = arr[i] & 0xFF;
        System.out.printf("%d\t", e);
        if ((e & 0x01) == 0) {
            sum += e;
        }
    }

    System.out.printf("\n sum=%d", sum);
}

打印结果:

0   1   2   3   4   5   ...[省略]...  251 252 253 254 255
 sum=16256

而C中一般都是无符号操作。在转成Java时,个人建议:
int转byte:直接强转
byte转int:用取位方法(&0xFF)

5、使用赋值运算符

使用位运算的赋值运算符,是位与位的操作,可以直接使用。
但如果是算术运算符,如+=, -=, *=, \=,那就要注意了。如byte的129(实际值为-127),

private static void test() {
    byte b = (byte) 0x81; // 实际-127,表示129
    b /= 3;
    System.out.printf("b=%d", b);
}

打印结果是:-42
但我们需要的结果是:43
因为我们使用了实际值进行运算,所以要达到想要的结果,必须先转换成大范围的数据类型,再运算:
b = (byte) ((b & 0xFF) / 3);

三、实例操作

1、byte[]转int

private static int bytes2Int(byte[] bs) {
    int retVal = 0;
    int len = bs.length < 4 ? bs.length : 4;
    for (int i = 0; i < len; i++) {
        retVal |= (bs[i] & 0xFF) << ((i & 0x03) << 3);
    }
    return retVal;

    // 如果确定足4位,可直接返回值
//        return (bs[0]&0xFF) | ((bs[1] & 0xFF)<<8) | ((bs[2] & 0xFF)<<16) | ((bs[3] & 0xFF)<<24);
}

2、int转byte[]

private static byte[] int2Bytes(int n) {
    byte[] bs = new byte[4];
    bs[0] = (byte) n;
    bs[1] = (byte) (n >> 8);
    bs[2] = (byte) (n >> 16);
    bs[3] = (byte) (n >> 24);
    return bs;
}

3、byte[]转int[]

private static int[] bytes2Ints(byte[] bs) {
    int len = (bs.length & 0x03) == 0 ? (bs.length >> 2) : (bs.length >> 2) + 1;
    int[] is = new int[len];

    // 进行转换前必须保证is[]所有元素的值都是0,这里创建时默认都是0,所以不用初始化赋值
    for (int i = 0; i < bs.length; ++i) {
        is[i >> 2] |= (bs[i] & 0xFF) << ((i & 0x03) << 3);
    }
    return is;
}

private static int[] bytes2Ints(byte[] bs) {
    int len = (bs.length & 0x03) == 0 ? (bs.length >> 2) : (bs.length >> 2) + 1;
    int[] is = new int[len];
    int offset = 0;

    for (int i = 0; i < len; i++, offset +=4) {
        int rest = 4;
        rest = offset + rest > bs.length ? bs.length - offset : rest;
        is[i] = 0;
        switch (rest) {
            case 4:
                is[i] |= (bs[offset + 3] & 0xFF) << 24;
            case 3:
                is[i] |= (bs[offset + 2] & 0xFF) << 16;
            case 2:
                is[i] |= (bs[offset + 1] & 0xFF) << 8;
            case 1:
                is[i] |= bs[offset] & 0xFF;
        }
    }
    return is;
}

4、int[]转byte[]

private static byte[] ints2Bytes(int[] is) {
    byte[] bs = new byte[is.length << 2];
    int offset;
    for (int i = 0; i < is.length; i++) {
        offset = i<<2;
        bs[offset] = (byte) (is[i] & 0xFF);
        bs[offset + 1] = (byte) ((is[i] >> 8) & 0xFF);
        bs[offset + 2] = (byte) ((is[i] >> 16) & 0xFF);
        bs[offset + 3] = (byte) ((is[i] >> 24) & 0xFF);
    }
    return bs;
}

有时会使用short,其与byte的互换,与上面同理。

猜你喜欢

转载自blog.csdn.net/a10615/article/details/51749321