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

2023-11-19

最近在把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的互换,与上面同理。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • Mockito when().thenReturn 不必要地调用该方法

    我正在研究继承的代码 我编写了一个应该捕获 NullPointerException 的测试 因为它试图从 null 对象调用方法 Test expected NullPointerException class public void c
  • Spring @RequestMapping 带有可选参数

    我的控制器在请求映射中存在可选参数的问题 请查看下面的控制器 GetMapping produces MediaType APPLICATION JSON VALUE public ResponseEntity
  • 你的CPU不支持NX

    我刚刚下载了 android studio 但是我遇到了一个问题 当我运行它时 它说你的 cpu 不支持 NX 我应该怎么办 NX 或实际上是 NX 处理器位 是处理器的一项功能 有助于保护您的 PC 免受恶意软件的攻击 当此功能未启用并且
  • 如何默认在 ActionOpenDocument 意图中显示“内部存储”选项

    我需要用户选择一个自定义文件类型的文件 并将其从 Windows 文件资源管理器拖到 Android 设备上 但默认情况下内部存储选项不可用 当我使用以下命令启动意图时 var libraryIntent new Intent Intent
  • 尝试在 ubuntu 中编译 android 内核时出错

    我正在尝试从源代码编译 Android 内核 并且我已经下载了所有正确的软件包来执行此操作 但由于某种原因我收到此错误 arm linux androideabi gcc error unrecognized command line op
  • 错误:在根项目“projectName”中找不到项目“app”

    我有一个在 Eclipse 中开发的旧应用程序 现在尝试将其迁移到 Android Studio 我更新了库并遵循了基本步骤 现在 我收到此错误 Error Project app not found in root project pro
  • 使用Caliper时如何指定命令行?

    我发现 Google 的微型基准测试项目 Caliper 非常有趣 但文档仍然 除了一些示例 完全不存在 我有两种不同的情况 需要影响 JVM Caliper 启动的命令行 我需要设置一些固定 最好在几个固定值之间交替 D 参数 我需要指定
  • 如何在控制器、服务和存储库模式中使用 DTO

    我正在遵循控制器 服务和存储库模式 我只是想知道 DTO 在哪里出现 控制器应该只接收 DTO 吗 我的理解是您不希望外界了解底层域模型 从领域模型到 DTO 的转换应该发生在控制器层还是服务层 在今天使用 Spring MVC 和交互式
  • 在 Mac 上正确运行基于 SWT 的跨平台 jar

    我一直致力于一个基于 SWT 的项目 该项目旨在部署为 Java Web Start 从而可以在多个平台上使用 到目前为止 我已经成功解决了由于 SWT 依赖的系统特定库而出现的导出问题 请参阅相关thread https stackove
  • Android Studio - Windows 7 上的 Android SDK 问题

    我对 Google i o 2013 上发布的最新开发工具 Android Studio 有疑问 我已经成功安装了该程序并且能够正常启动 我可以导入现有项目并对其进行编辑 但是 当我尝试单击 SDK 管理器图标或 AVD 管理器图标时 或者
  • 无法捆绑适用于 Mac 的 Java 应用程序 1.8

    我正在尝试将我的 Java 应用程序导出到 Mac 该应用程序基于编译器合规级别 1 7 我尝试了不同的方法来捆绑应用程序 1 日食 我可以用来在 Eclipse 上导出的最新 JVM 版本是 1 6 2 马文 看来Maven上也存在同样的
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • Android向menuItem添加子菜单,addSubMenu()在哪里?

    我想根据我的参数以编程方式将 OptionsMenu 内的子菜单添加到 menuItem 中 我检查了android sdk中的 MenuItem 没有addSubMenu 方法 尽管你可以找到 hasSubMenu 和 getSubMen
  • 如何根据 gradle 风格设置变量

    我想传递一个变量test我为每种风格设置了不同的值作为 NDK 的定义 但出于某种原因 他总是忽略了最后味道的价值 这是 build gradle apply plugin com android library def test andr
  • 如何确定对手机号码的呼叫是本地呼叫还是 STD 或 ISD

    我正在为 Android 开发某种应用程序 但不知道如何获取被叫号码是本地或 STD 的号码的数据 即手机号码检查器等应用程序从哪里获取数据 注意 我说的是手机号码 而不是固定电话 固定电话号码 你得到的数字是字符串类型 因此 您可以获取号
  • 静态变量的线程安全

    class ABC implements Runnable private static int a private static int b public void run 我有一个如上所述的 Java 类 我有这个类的多个线程 在里面r
  • 编译器抱怨“缺少返回语句”,即使不可能达到缺少返回语句的条件

    在下面的方法中 编译器抱怨缺少退货声明即使该方法只有一条路径 并且它包含一个return陈述 抑制错误需要另一个return陈述 public int foo if true return 5 鉴于Java编译器可以识别无限循环 https
  • 一次显示两条Toast消息?

    我希望在一个位置显示一条 Toast 消息 并在另一位置同时显示另一条 Toast 消息 多个 Toast 消息似乎总是按顺序排队和显示 是否可以同时显示两条消息 是否有一种解决方法至少可以提供这种外观并且不涉及扰乱活动布局 Edit 看来
  • 实现滚动选择 ListView 中的项目

    我想使用 ListView 您可以在其中滚动列表来选择一个项目 它应该像一个 Seekbar 但拇指应该是固定的 并且您必须使用该栏来调整它 我面临的一个问题是 我不知道这种小部件是如何调用的 这使得我很难搜索 所以我制作了下面这张图片 以
  • android sdk 的位置尚未在 Windows 操作系统的首选项中设置

    在 Eclipse 上 我转到 windows gt Android SDK 和 AVD Manager 然后弹出此消息 Android sdk 的位置尚未在首选项中设置 进入首选项 在侧边栏找到 Android 然后会出现一个 SDK 位

随机推荐