Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JNA数组和指针_Java_C_Jna - Fatal编程技术网

Java JNA数组和指针

Java JNA数组和指针,java,c,jna,Java,C,Jna,我现在正在做一个项目,它要求我接收来自C库的Java调用。基本上我调用一个接受函数指针的C函数,然后C函数使用函数指针作为回调。我使用JNA传递一个Java对象(从现在起我将称之为回调对象)作为回调函数。回调对象有一个方法,该方法接收包含16个元素字节数组和其他变量的C结构(称为Frame)。我以标准JNA方式将此结构包装到Java类中,如下所示: class Frame extends Structure { public short port; public short fl

我现在正在做一个项目,它要求我接收来自C库的Java调用。基本上我调用一个接受函数指针的C函数,然后C函数使用函数指针作为回调。我使用JNA传递一个Java对象(从现在起我将称之为回调对象)作为回调函数。回调对象有一个方法,该方法接收包含16个元素字节数组和其他变量的C结构(称为Frame)。我以标准JNA方式将此结构包装到Java类中,如下所示:

class Frame extends Structure {
    public short port;
    public short flags;
    public Pointer name // this is a pointer to the byte array
    public int rateDivisor;
}
回调机制工作正常!回调对象从C接收一个帧对象,但当我尝试使用
name.getByteArray(0,16)
从指针获取字节数组时,应用程序崩溃,出现访问冲突异常。但是,如果我用字节数组替换指针:

class Frame extends Structure {
    public short port;
    public short flags;
    public byte[] name = new byte[16];
    public int rateDivisor;
}
那么代码就可以正常工作了!但是,我有一个很好的理由不想使用此代码。每次调用函数时,返回的帧实际上是同一个对象(它只是被重用),但字节数组是一个新对象。这个回调函数每秒被调用多次,这会导致垃圾收集器疯狂地吞噬数千个临时数组。这个项目对性能至关重要,所以我真的不希望在应用程序的这一部分中有任何临时对象

我的猜测是,通过在Frame类中使用字节数组,我使JNA创建了C字节数组的新副本。我想做的是有一个指向C字节数组的指针,这样就不需要复制数据,也就不用进行JNA指针的实验了

我的问题是为什么我不能从指针获取字节数组?任何帮助都将不胜感激:)


(注意:让这更困难的是我没有访问C源代码的权限!!)

原因可能很简单:name不是C结构中的指针

让我举两个例子:

C:

映射到Java:

class MyJStruct extends Structure{
  Pointer name;
}
class MyJStruct extends Structure{
  byte[] name = new byte[16];
}
但在另一个场景中(我认为你陷入其中):

C:

映射到Java:

class MyJStruct extends Structure{
  Pointer name;
}
class MyJStruct extends Structure{
  byte[] name = new byte[16];
}
在第一种情况下,C结构保存一个指针,它的sizeof是指针的大小(4或8字节,取决于32/64位),在第二种情况下,它保存整个数组,这意味着它的大小是16字节

这解释了两个Java映射之间的差异:JNA需要知道如何读取结构字段,并且还解释了在执行以下操作时调用
name.getByteArray(0,16)
时出错的原因:

  • 标志
    字段后的前4个字节
  • 将其视为指针
  • 进入ram到指定地址并读取16字节
  • 由于指向的内存区域可能超出了程序的范围,因此崩溃

    您可以自己在C中检查它:如果
    sizeof(struct Frame)
    是24,那么该结构包含整个数组如果它是12(或16),那么它包含一个32位指针(或64位指针)

    关于这件事的官方文件非常清楚,但很难找到,所以你可以这样做:

    您可以在“嵌套数组”下找到它,我鼓励您阅读“可变大小结构”下面的部分

    希望这有帮助

    问候

    (满足StackTrace期望的新答案;请参阅对我先前答案的评论)

    嗯,我不确定是否有一个好的和干净的方式来做这件事。。。因此,我将指出一个很好的游戏,叫做
    内存
    指针
    游戏

    请记住,您可能需要以下方法(C):

    在我看来,最糟糕的情况是第三种情况,因为如果没有前面解释的法线映射,就无法实现。下一个最坏的情况是第一个,因为您不会在RAM中获得位置,而是在堆栈顶部获得直接结果,因此读取它需要您欺骗JNA,以便能够获取指针并读取要跳过的内存区域(Java)之后的内容:

    我想你明白了。如果你的结构更大,只需将它分成几个小部分,避免你想跳过的区域,然后在第一部分指向的内存中手动导航,重新连接它们

    以这种方式工作时要记住的事项:

  • 指针大小32/64位--4或8字节请参见
    com.sun.jna.Native.Pointer\u size
  • 我还没有检查这个答案

  • 将回调的输入参数更改为指针。维护您自己的私有指针->帧映射(有或没有弱引用),并且仅在没有输入指针的情况下基于输入指针创建新帧

    e、 g

    Map frames=newhashmap();
    无效回调(指针p){
    Frame f=frames.get(p);
    如果(f==null){
    f=新帧(p);
    帧。put(p,f);
    }
    //做任何事。。。
    }
    
    谢谢你的文档,它非常有用。您的答案解释了为什么Frame的字节数组版本可以工作,但我认为C数组是用指针实现的,因此尝试使用指针?不管怎样,您知道有什么方法可以阻止JNA每次C向Java发送帧对象时创建一个新数组吗?这才是我真正的问题。
    Frame nativeLibFunc(...);        //1
    Frame * nativeLibFunc(...);      //2
    void nativeLibFunc(Frame f);     //3
    void nativeLibFunc(Frame * fptr);//4
    
    class MyMicroFrame extends Structure {
        public short port;
        public short flags;
        //public byte[] name = new byte[16]; ==> commented out, ignored
        //public int rateDivisor;            ==> commented out, read manually, see below
    }
    //...
    MyMicroFrame mmf = NativeLib.INSTANCE.nativeLibFunc(...);
    int rateDivisor = mmf.getPointer().getInt(20);
    
    Map frames = new HashMap<Pointer,Frame>();
    
    void callback(Pointer p) {
       Frame f = frames.get(p);
       if (f == null) {
           f = new Frame(p);
           frames.put(p, f);
       }
       // do whatever...
    }