JNI便捷开发框架JNA框架之指针参数Pointer(二)

一、背景

上一篇介绍了JNA框架开发的入门,项目是基于JNA框架开发,本文的焦点是参数的地址传递。
在java中都是值传递,但是因为使用JNA框架,目标函数是C/C++是有地址变量的,很多时候都需要将变量的结果带回,因此,地址传递在JNA项目中几乎是必须的。

二、案例代码

C/C++

/**
 * 返回a+b的值
 * 同时c和msg通过参数返回
 */
int add(int a, int b, int *c, char **msg) {
    *c = (a + b) * 2;
    char *string = "hello world!";
    *msg = string;
    return a + b;
}

如果java这样写

public class HelloJNA {

    /**
     * 定义一个接口,默认的是继承Library ,如果动态链接库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary
     * 这个接口对应一个动态链接(SO)文件
     */
    public interface LibraryAdd extends Library {
        // 这里使用绝对路径加载
        LibraryAdd LIBRARY_ADD = Native.load("/program/cpp/libhello.so", LibraryAdd.class);
        int add(int a, int b, int c, String msg);
    }

    public static void main(String[] args) {
        int c = 0;
        String msg = "start";
        // 调用so映射的接口函数
        int add = LibraryAdd.LIBRARY_ADD.add(10, 15, c, msg);
        System.out.println("相加结果:" + add);
    }
}

那么不管add函数对c和msg做了何种改变,返回java中,值都不会被变更。

三、指针参数Pointer

那么如何实现类似C语言那样的地址传递,或者说指针传递呢?在JNA框架中,我们可以借助一个类完成,他就是Pointer。
com.sun.jna.Pointer,指针数据类型,用于匹配转换映射函数的指针变量。

1、定义
Pointer c = new Memory(50);
Pointer msg = new Memory(50);

说明:
这样的指针变量定义很像C的写法,就是在定义的时候申请空间。比如这里就申请50个空间
根据测试结果对于字符串,一个空间对于两个字符左右。如果返回的结果长度比分配的空间大,则会报错

Exception in thread “main” java.lang.IndexOutOfBoundsException: Bounds exceeds available space : size=6, offset=8

最后可以这样释放申请的空间

Native.free(Pointer.nativeValue(c));     //手动释放内存
Pointer.nativeValue(c, 0);      		//避免Memory对象被GC时重复执行Nativ.free()方法
Native.free(Pointer.nativeValue(msg));   
Pointer.nativeValue(msg, 0);      
2、使用
import com.sun.jna.Library;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

/**
 * 一个java类
 * 演示指针传输指针变量
 */
public class HelloJNA_Pointer {

    /**
     * 定义一个接口,默认的是继承Library ,如果动态链接库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary
     * 这个接口对应一个动态链接(SO)文件
     */
    public interface LibraryAdd extends Library {

        LibraryAdd LIBRARY_ADD = Native.load("/program/cpp/libhello.so", LibraryAdd.class);

        /**
         * 指针变量,用Pointer类型定义
         * c是int*
         * msg是char**
         */
        int add_c(int a, int b, Pointer c, Pointer msg);

    }

    public static void main(String[] args) {

        Pointer c = new Memory(50);
        Pointer msg = new Memory(8);
        // 调用so映射的接口函数
        int add = LibraryAdd.LIBRARY_ADD.add_c(10, 15, c, msg);
        System.out.println("相加结果:" + add);

        // 指针变量
        System.out.println("c的值:" + c.getInt(0));
        // 这样才能拿到
        System.out.println("msg的值:" + msg.getPointer(0).getString(0));

        Native.free(Pointer.nativeValue(c));   //手动释放内存
        Pointer.nativeValue(c, 0);      //避免Memory对象被GC时重复执行Nativ.free()方法

        Native.free(Pointer.nativeValue(msg));   //手动释放内存
        Pointer.nativeValue(msg, 0);      //避免Memory对象被GC时重复执行Nativ.free()方法
    }
}

说明:
①、传递参数直接传Pointer 定义出来的对象即可
②、取值时,需要根据变量的类型采用不同的API读取,例如,如果函数是int*变量,那么就c.getInt(0)
如果是char **msg,就msg.getPointer(0).getString(0)
③、指针说明:一层指针就直接取值c.getInt(0),其中0表示偏移量从0开始;如果是二级指针,msg.getPointer(0).getString(0),表示指针的指针再取值;多级指针以此类推。

3、运行结果
[root@192 cpp]# java -jar JNATestC.jar 
相加结果:25
c的值:50
msg的值:hello world!

四、最后

项目搭建请回看:JNI便捷开发框架JNA框架之入门(一)
有个知识点参考:C语言中字符串变量的函数值传递与指针传递

上一篇 JNI便捷开发框架JNA框架之入门(一)
下一篇JNI便捷开发框架JNA框架之参数引用传递ByReference(三)

你可能感兴趣的:(JNI专题,Pointer,java,c语言,c++)