JNA调用自制.so库触发IndexOutOfBoundsException的数组声明问题
解决JNA调用C++库时结构体数组的越界问题
你的问题核心是用ByValue结构体实例调用toArray()方法时的内存分配错误,下面我一步步拆解问题并给出解决方案:
为什么会报错?
你原来的代码里写了:
WGInputs.ByValue jnaInputs [] = (WGInputs.ByValue[])new WGInputs.ByValue().toArray(2);
ByValue类型的结构体本身只占用单个结构体大小的内存空间(报错信息里显示是32字节)。当你调用toArray(2)时,JNA会尝试从这个实例的内存地址开始,开辟能容纳2个结构体的空间,但第二个结构体的起始偏移是32字节,结束位置是64字节,已经超出了这个实例原本的内存范围,所以直接触发了IndexOutOfBoundsException。而数组大小设为1时,刚好在内存范围内,所以不会报错。
正确的结构体数组初始化方式
你需要换一种方式创建结构体数组,让JNA为整个数组分配连续的内存块,而不是在单个ByValue实例的内存里挤空间。
步骤1:修正数组初始化
不要用ByValue实例来调用toArray(),改用普通的Structure(默认是ByReference类型)来创建数组:
// 让JNA分配能容纳2个WGInputs结构体的连续内存 WGInputs[] jnaInputs = (WGInputs[]) new WGInputs().toArray(2);
步骤2:初始化数组中的每个元素
接下来逐个初始化数组里的结构体,注意如果嵌套了ByValue类型的子结构体,直接赋值后需要确保数据写入内存(可以手动调用write()方法):
// 初始化第一个元素 WGInputs input0 = jnaInputs[0]; Measure.ByValue m0 = new Measure.ByValue(); m0.valeur = 18; Date.ByValue d0 = new Date.ByValue(); d0.m_day = 10; d0.m_month = 10; d0.m_year = 10; d0.m_hour = 12; d0.m_min = 12; d0.m_sec = 12; m0.date = d0; input0.m = m0; input0.write(); // 将结构体数据同步到内存中 // 初始化第二个元素(和第一个类似) WGInputs input1 = jnaInputs[1]; Measure.ByValue m1 = new Measure.ByValue(); m1.valeur = 18; Date.ByValue d1 = new Date.ByValue(); d1.m_day = 10; d1.m_month = 10; d1.m_year = 10; d1.m_hour = 12; d1.m_min = 12; d1.m_sec = 12; m1.date = d1; input1.m = m1; input1.write();
步骤3:确保JNA接口的函数声明正确
检查你的InterfaceLibWestgard接口中,WGevaluateWithListOnly的参数类型是否匹配,应该声明为结构体数组类型:
public interface InterfaceLibWestgard extends Library { InterfaceLibWestgard INSTANCE = Native.load("your-library-name", InterfaceLibWestgard.class); // 参数对应C++的Measure数组,这里用WGInputs[](根据你的结构体对应关系调整) void WGevaluateWithListOnly(WGInputs[] inputs, float mean, float sd); }
额外注意点
- 确保Java结构体的字段顺序和C++完全一致,建议用
@FieldOrder注解显式指定,避免内存布局不匹配:@FieldOrder({"valeur", "date"}) public class Measure extends Structure { public float valeur; public Date.ByValue date; public static class ByValue extends Measure implements Structure.ByValue {} } - 如果C函数确实期望传入
ByValue类型的数组,你可以在JNA接口中将参数声明为WGInputs.ByValue[],但初始化时同样需要依赖JNA分配连续内存,更推荐用普通结构体数组,因为ByReference更符合C数组的传递逻辑。
修正后的完整Java代码示例
public class Westgard { static { System.setProperty("jna.library.path","../logic/resources/calculator"); } public static void main(String[] args) { // 正确创建结构体数组 WGInputs[] jnaInputs = (WGInputs[]) new WGInputs().toArray(2); // 初始化第一个元素 WGInputs input0 = jnaInputs[0]; Measure.ByValue m0 = new Measure.ByValue(); m0.valeur = 18; Date.ByValue d0 = new Date.ByValue(); d0.m_day = 10; d0.m_month = 10; d0.m_year = 10; d0.m_hour = 12; d0.m_min = 12; d0.m_sec = 12; m0.date = d0; input0.m = m0; input0.write(); // 初始化第二个元素 WGInputs input1 = jnaInputs[1]; Measure.ByValue m1 = new Measure.ByValue(); m1.valeur = 18; Date.ByValue d1 = new Date.ByValue(); d1.m_day = 10; d1.m_month = 10; d1.m_year = 10; d1.m_hour = 12; d1.m_min = 12; d1.m_sec = 12; m1.date = d1; input1.m = m1; input1.write(); // 调用C++库函数 InterfaceLibWestgard.INSTANCE.WGevaluateWithListOnly(jnaInputs, 12, 1); } }
这样修改后,JNA会为整个数组分配足够的连续内存,就不会再出现越界错误了。
内容的提问来源于stack exchange,提问作者Charles Socié
相关产品推荐
相关产品推荐

