如何使用Java FFM API传递void**类型的参数?
如何使用Java FFM API传递void**类型参数?
原生C函数定义
void initHandle(void* handle); void useHandles(void** handles);
你的问题代码
var LINKER = Linker.nativeLinker(); var SYM_LOOKUP = SymbolLookup.loaderLookup().or(LINKER.defaultLookup()); var INIT_HANDLE = LINKER.downcallHandle(SYM_LOOKUP.find("init_handle").orElseThrow(), FunctionDescriptor.ofVoid(ADDRESS)); var USE_HANDLES = LINKER.downcallHandle(SYM_LOOKUP.find("use_handles").orElseThrow(), FunctionDescriptor.ofVoid(ADDRESS)); try (var arena = Arena.ofConfined()) { List<MemorySegment> handleList = new ArrayList<>(); for (int i = 0; i < 5; i++) { var handle = arena.allocate(ADDRESS); INIT_HANDLE.invokeExact(handle); handleList.add(handle); } // ... var handlesHandle = arena.allocate(ADDRESS, handleList.size()); for (int i = 0; i < handleList.size(); i++) { handlesHandle.setAtIndex(ADDRESS, i, handleList.get(i).get(ADDRESS, 0)); } USE_HANDLES.invokeExact(handlesHandle.get(ADDRESS, 0)); // 这里出错 }
问题原因与修正
你调用USE_HANDLES时的参数传递错误:
void** handles本质是指向指针数组的指针,对应Java FFM里的MemorySegment(表示数组的首地址)。- 你代码里的
handlesHandle.get(ADDRESS, 0)是取了指针数组第一个元素的值(也就是第一个句柄的地址),这并不是useHandles需要的数组指针本身。
修正后的完整代码如下:
var LINKER = Linker.nativeLinker(); var SYM_LOOKUP = SymbolLookup.loaderLookup().or(LINKER.defaultLookup()); var INIT_HANDLE = LINKER.downcallHandle(SYM_LOOKUP.find("init_handle").orElseThrow(), FunctionDescriptor.ofVoid(ADDRESS)); var USE_HANDLES = LINKER.downcallHandle(SYM_LOOKUP.find("use_handles").orElseThrow(), FunctionDescriptor.ofVoid(ADDRESS)); try (var arena = Arena.ofConfined()) { List<MemorySegment> handleList = new ArrayList<>(); for (int i = 0; i < 5; i++) { var handle = arena.allocate(ADDRESS); INIT_HANDLE.invokeExact(handle); handleList.add(handle); } // 分配存储指针数组的内存,大小为handleList.size()个ADDRESS类型 var handlesArray = arena.allocate(ADDRESS, handleList.size()); // 将每个初始化后的句柄地址写入数组 for (int i = 0; i < handleList.size(); i++) { // 取出initHandle设置的实际句柄地址 MemoryAddress handleAddr = handleList.get(i).get(ADDRESS, 0); handlesArray.setAtIndex(ADDRESS, i, handleAddr); } // 直接传递数组的首地址(对应void**)给useHandles USE_HANDLES.invokeExact(handlesArray); }
关键说明
arena.allocate(ADDRESS, handleList.size()):分配一块内存,用来存储handleList.size()个指针(每个指针大小是ADDRESS),对应原生的void*[]数组。handlesArray本身就是这个数组的首地址,完全匹配原生的void**类型,直接传给useHandles即可。- 你之前填充数组的逻辑是正确的(取出每个
handle里存储的实际句柄地址写入数组),问题仅在于最后一步的参数传递。
内容的提问来源于stack exchange,提问作者Eng.Fouad
相关产品推荐
相关产品推荐

