当 sizeof(long) 为 4 时,为什么我需要为 JNA 指针保留超过 4 个字节才能接收 long?

Why do I need to reserve more than 4 bytes for a JNA pointer to receive a long when sizeof(long) is 4?

我正在使用 JNA 与使用 Visual Studio 2015 编译的本机 C 库进行通信。我正在使用 64 位计算机。我正在尝试通过长指针 long *pdays 参数接收 C 函数的值。我收到以下异常:

java.lang.IndexOutOfBoundsException: Bounds exceeds available space : size=4, offset=8 at com.sun.jna.Memory.boundsCheck(Memory.java:220) at com.sun.jna.Memory.getLong(Memory.java:498)

我不明白我在这里遗漏了什么,如果我只为指针保留 4 个字节的内存会导致上面的崩溃,但是如果我保留 8 个字节则一切正常。但是sizeof(long) returns 4,那为什么要预留超过4个字节呢?

    System.out.println(NativeLong.SIZE); // --> 4
    System.out.println(Native.LONG_SIZE); // --> 4

    // Pointer pDays = new Memory(Native.LONG_SIZE); Results in IndexOutOfBoundsException
    Pointer pDays = new Memory(8); // 

    nativeLib.GetDaysUntilExpiration(pDays);
    return pDays.getLong(0); // crashes here when reserving just 4 bytes

它崩溃是因为您试图从只分配了 4 个字节的本机内存中读取 8 个字节。

无论原生类型是什么,或者只有 4 个字节都没有关系。 Memory 仅包含 4 个字节,您可以按照您希望的任何方式进行解释。您可以得到一个 byte[] 数组,或一个 int (具有这 4 个字节)甚至 shortbyte 只读取该字节数。您甚至可以尝试 String(尽管没有空终止符,您可能会读取比允许的 4 个字节更多的内容,谁知道您会得到什么,所以这很危险。)

您要求获取一个Java long,这是一个8字节的变量;因此,代码会检查偏移量的下一个 8 个字节是否适合分配的内存。来自 Memory.java 的代码具有以下硬编码:

boundsCheck(offset, 8);

javadoc 很清楚这是为什么:

Indirect the native pointer to malloc space, a la Pointer.getLong. But this method performs a bounds checks to ensure that the indirection does not cause memory outside the malloced space to be accessed.

不手动分配 space 的正确方法是简单地使用 NativeLongByReference。 JNA 会自行处理 space 的分配和值的检索,您不必担心原始大小。

NativeLongByReference pDays = new NativeLongByReference();
nativeLib.GetDaysUntilExpiration(pDays);
return pDays.getValue().longValue(); 

编辑:我在你的评论中注意到你说 "The C function argument is a pointer, using a NativeLongByReference would result in a "LongByReference cannot be converted to Pointer”——这不是 C 函数的问题,而是接口中 JNA 映射的问题。最好更改GetDaysUntilExpiration 的 JNA 映射采用 NativeLongByReference 参数。如果您不能更改该函数的 JNA 映射,则可以通过使用 pDays.getPointer() 作为参数来解决它。