上一篇介绍了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中,值都不会被变更。
那么如何实现类似C语言那样的地址传递,或者说指针传递呢?在JNA框架中,我们可以借助一个类完成,他就是Pointer。
com.sun.jna.Pointer,指针数据类型,用于匹配转换映射函数的指针变量。
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);
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),表示指针的指针再取值;多级指针以此类推。
[root@192 cpp]# java -jar JNATestC.jar
相加结果:25
c的值:50
msg的值:hello world!
项目搭建请回看:JNI便捷开发框架JNA框架之入门(一)
有个知识点参考:C语言中字符串变量的函数值传递与指针传递
上一篇 JNI便捷开发框架JNA框架之入门(一)
下一篇JNI便捷开发框架JNA框架之参数引用传递ByReference(三)