将char*(数组)转换为unsigned long + 1?

4

我正在尝试将一些C代码移植到Java,但是我一直在努力弄清楚这些代码的作用。

注意: rawEntry 的类型是 char*,似乎是分配了 12 个字节的空间。

char *rawEntry = (char*)malloc(0x000c);

unsigned long *l;
unsigned long offset;

// ...

l = (unsigned long*) rawEntry + 1;
offset = ntohl(*l);

据我所知,它会取数组的前四个项,将它们组合成一个长整数,然而我的Java尝试并不成功。
offset = (rawEntry[0] << 24) +
         (rawEntry[1] << 16) +
         (rawEntry[2] << 8) +
         (rawEntry[3]) +
         1;

当面对以下数组时:
1 0 0 0 0 0 0 0 0 0 11 -38

这段C代码输出的偏移量为3034
我的Java代码输出16777217,或者在翻转字节序后输出1


rawEntry 可以是 char * 或者 char 数组,但不能同时存在。这个区别对于你的特定任务可能并不重要,但也有可能很重要。因为你没有给我们足够的信息,所以很难判断。 - John Bollinger
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - CraftedCart
2个回答

5
这个表达式。
l = (unsigned long*) rawEntry + 1;

rawEntry转换为指向在您的系统上大小为8个字节的类型的指针。之后,添加1意味着添加8个字节,因此实际转换如下所示:

offset = (Byte.toUnsignedInt(rawEntry[ 8]) << 24) +
         (Byte.toUnsignedInt(rawEntry[ 9]) << 16) +
         (Byte.toUnsignedInt(rawEntry[10]) <<  8) +
         (Byte.toUnsignedInt(rawEntry[11]) <<  0);

以下四个字节被解释为3034:
0 0 11 -38

您可以通过使用ByteBuffer来进一步简化此过程:
int offset = ByteBuffer.wrap(rawEntry, 8, 4).getInt();

相反,类型转换运算符的优先级高于加法。您突出显示的表达式等同于((unsigned long*) rawEntry) + 1。当然,我们必须注意指针加法的语义。 - John Bollinger
@JohnBollinger 您是正确的。从他的输出结果反推,看起来在 OP 的系统上 unsigned long 的大小为 8。 - Sergey Kalinichenko
1
@CraftedCart 这是因为你将有符号的 byte 转换为有符号的 int。在 rawEntry 的每个元素周围使用 Byte.toUnsignedInt - Marko Topolnik
然后,接下来的一行代码是 l = (unsigned long*) rawEntry + 2。所以... rawEntry[16]... 这个根本不存在... - 文档中有一个小表格,说明它期望在偏移量4处有一个值(而不是8)... 或许开发这个程序的人总是期望long类型是4个字节?... 这就解释了为什么这个东西总是让我崩溃。 - CraftedCart
@CraftedCart 是的,这是完全可能的。看起来读取网络数据的代码依赖于硬件。顺便说一句,看一下编辑,这应该是从字节缓冲区中读取“int”的更简单的方法。如果您按顺序读取数据,则应该能够通过“ByteBuffer”完成所有读取操作。 - Sergey Kalinichenko
显示剩余2条评论

2
在继续之前,需要注意这段代码依赖于您的平台中 long 的实际大小。在64位平台上,long 占用8个字节,即64位。另一方面,ntohl 转换的是一个32位长(4个字节)的“网络长”。
假设 rawEntry{1,0,0,0,0,0,0,0,0,0,0xB,0xDA,…} (0xB 是16进制中11的表示,0xDA是16进制无符号值,对应于-38)。
l = (unsigned long*) rawEntry + 1;

实际上意味着l是指第二个unsigned long。也就是从rawEntry跳过前8个字节,l就指向了{0,0,0xB,0xDA,…}部分。现在*l就是由{0,0,0xB,0xDA,…}字节序列表示的unsigned long,它依赖于你的架构。如果你在一个小端机器上,那么它将是0x…DA0B0000(省略号表示数组结尾未知,即未定义行为)。现在ntohl只会保留最后32位并反转字节顺序,结果为0x00000BDA或10进制3034。

在Java中编写此代码的简单方法如下:

offset = (rawEntry[8] << 24) +
         (rawEntry[9] << 16) +
         (rawEntry[10] << 8) +
         (rawEntry[11]);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接