You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Java Foreign Function & Memory API获取C++结构体返回值

问题:无法读取C++返回的Point结构体中的值

我在调用C++返回结构体的函数时,无法解析结构体中的值,相关代码和错误信息如下:

C++结构体定义

struct Point {
    int x;
    int y;
};

C++函数实现

Point test_point(Point points[],long count)
{
    if (count <= 0) {
        Point defaultPoint = { 0, 0 };
        return defaultPoint;
    }

    Point maxPoint = points[0];
    int maxSum = maxPoint.x + maxPoint.y;

    for (int i = 1; i < count; ++i) {
        int currentSum = points[i].x + points[i].y;
        if (currentSum > maxSum) {
            maxSum = currentSum;
            maxPoint = points[i];
        }
    }

    int x = maxPoint.x;
    int y = maxPoint.y;
    std::cout << "x = " << x << ", y = " << y << std::endl;
    return maxPoint;
}

Java调用代码

public static void dereferenceSegmentsStruct() throws Throwable {
    Linker linker = Linker.nativeLinker();
    SymbolLookup symbolLookup = SymbolLookup.loaderLookup();
    MethodHandle test_point = linker.downcallHandle(
            symbolLookup.find("test_point").orElseThrow(),
            FunctionDescriptor.of(ADDRESS, ADDRESS, JAVA_LONG)
    );

    StructLayout structLayout = MemoryLayout.structLayout(
            JAVA_INT.withName("x"),
            JAVA_INT.withName("y"));

    SequenceLayout ptsLayout = MemoryLayout.sequenceLayout(10, structLayout);

    VarHandle xHandle    // (MemorySegment, long) -> int
            = ptsLayout.varHandle(PathElement.sequenceElement(),
            PathElement.groupElement("x"));
    VarHandle yHandle    // (MemorySegment, long) -> int
            = ptsLayout.varHandle(PathElement.sequenceElement(),
            PathElement.groupElement("y"));

    MemorySegment segment = Arena.ofAuto().allocate(ptsLayout);
    for (int i = 0; i < ptsLayout.elementCount(); i++) {
        xHandle.set(segment,
                /* index */ (long) i,
                /* value to write */ i); // x
        yHandle.set(segment,
                /* index */ (long) i,
                /* value to write */ i); // y
    }

    MemorySegment result = (MemorySegment) test_point.invoke(segment, ptsLayout.elementCount());
    result = result.reinterpret(structLayout.byteSize());
    System.out.println(result.getAtIndex(JAVA_INT, 0));
}

错误信息

Exception in thread "main" java.lang.IllegalArgumentException: Misaligned access at address: 38654705673
    at java.base/java.lang.invoke.VarHandleSegmentViewBase.newIllegalArgumentExceptionForMisalignedAccess(VarHandleSegmentViewBase.java:57)
    at java.base/java.lang.invoke.VarHandleSegmentAsInts.offsetNoVMAlignCheck(VarHandleSegmentAsInts.java:100)
    at java.base/java.lang.invoke.VarHandleSegmentAsInts.get(VarHandleSegmentAsInts.java:111)
    at java.base/java.lang.foreign.MemorySegment.getAtIndex(MemorySegment.java:1921)
    at org.example/org.example.TestNative.dereferenceSegmentsStruct(TestNative.java:109)
    at org.example/org.example.TestNative.main(TestNative.java:33)

解决方案

问题根源

你错误地将C++函数返回的结构体值当成了指针(ADDRESS),导致结构体的二进制数据被解析成了无效的内存地址,进而触发对齐错误和非法内存访问。

修正步骤

  1. 修改函数描述符的返回类型
    将返回类型从ADDRESS替换为定义好的structLayout,明确告诉Java Foreign Function API,函数返回的是结构体值而非指针:

    MethodHandle test_point = linker.downcallHandle(
            symbolLookup.find("test_point").orElseThrow(),
            FunctionDescriptor.of(structLayout, ADDRESS, JAVA_LONG) // 第一个参数是返回类型
    );
    
  2. 显式指定结构体对齐(可选但推荐)
    确保Java侧的结构体对齐与C++侧一致,避免对齐问题:

    StructLayout structLayout = MemoryLayout.structLayout(
            JAVA_INT.withName("x"),
            JAVA_INT.withName("y")
    ).withByteAlignment(4); // 匹配C++中int的默认对齐
    
  3. 正确读取结构体字段
    调用函数后,返回的MemorySegment直接包含结构体的二进制数据,可通过字段偏移或VarHandle读取值:

    MemorySegment result = (MemorySegment) test_point.invoke(segment, ptsLayout.elementCount());
    
    // 方式1:通过字段偏移读取
    int x = result.get(JAVA_INT, structLayout.byteOffset(PathElement.groupElement("x")));
    int y = result.get(JAVA_INT, structLayout.byteOffset(PathElement.groupElement("y")));
    
    // 方式2:通过VarHandle读取(更直观)
    VarHandle xHandle = structLayout.varHandle(PathElement.groupElement("x"));
    VarHandle yHandle = structLayout.varHandle(PathElement.groupElement("y"));
    int x = (int) xHandle.get(result);
    int y = (int) yHandle.get(result);
    
    System.out.println("x = " + x + ", y = " + y);
    

完整修正后的Java代码

public static void dereferenceSegmentsStruct() throws Throwable {
    Linker linker = Linker.nativeLinker();
    SymbolLookup symbolLookup = SymbolLookup.loaderLookup();

    StructLayout structLayout = MemoryLayout.structLayout(
            JAVA_INT.withName("x"),
            JAVA_INT.withName("y")
    ).withByteAlignment(4);

    MethodHandle test_point = linker.downcallHandle(
            symbolLookup.find("test_point").orElseThrow(),
            FunctionDescriptor.of(structLayout, ADDRESS, JAVA_LONG)
    );

    SequenceLayout ptsLayout = MemoryLayout.sequenceLayout(10, structLayout);

    VarHandle xHandle = ptsLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("x"));
    VarHandle yHandle = ptsLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("y"));

    MemorySegment segment = Arena.ofAuto().allocate(ptsLayout);
    for (int i = 0; i < ptsLayout.elementCount(); i++) {
        xHandle.set(segment, (long) i, i);
        yHandle.set(segment, (long) i, i);
    }

    MemorySegment result = (MemorySegment) test_point.invoke(segment, ptsLayout.elementCount());

    // 使用VarHandle读取字段
    VarHandle resultXHandle = structLayout.varHandle(PathElement.groupElement("x"));
    VarHandle resultYHandle = structLayout.varHandle(PathElement.groupElement("y"));
    int x = (int) resultXHandle.get(result);
    int y = (int) resultYHandle.get(result);

    System.out.println("x = " + x + ", y = " + y);
}

内容的提问来源于stack exchange,提问作者jdragon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 04:39:56