【问题标题】:Julia - C types with struct hack cause segfaultJulia - 带有 struct hack 的 C 类型导致段错误
【发布时间】:2017-03-07 20:12:14
【问题描述】:

这个问题是Julia - C interface with nonfundamental types 的后续问题,有足够的新信息被视为另一个问题。我使用的 C 库有两种类似的类型,以及 mystruct 的三种可能版本:

struct contained {
    int x;
    int y;
    int z;
};
struct mystruct {
    int n;
//original:
//    contained* arr;
//struct hack version 1:
//    contained arr[1];
//struct hack version 2:
      contained arr[];
};

使用原始问题的答案,我定义了以下相应的 Julia 类型,它们适用于 mystruct 的原始版本,但不适用于使用 struct hack 的任何一个版本:

type contained
    x::Cint
    y::Cint
    z::Cint
end
type mystruct
    n::Cint
    arr::Ptr{contained}
end

如果我从 Julia 调用返回 Ptr{mystruct} 的 C 函数并将其保存为 ms,我可以输入 m = unsafe_load(ms) 并查看 m.n 和指针 m.arr,但我只能检查它的值在原始情况下使用unsafe_load(m.arr)。否则 unsafe_load(m.arr) 会导致段错误。 Julia 中处理包含以这种方式定义的可变长度数组的 C 结构的正确方法是什么?我只会使用mystruct 的原始定义和contained *arr,因为它可以按预期工作,但我也需要适用于其他情况的代码。

【问题讨论】:

    标签: c struct julia


    【解决方案1】:

    C interface section of the Julia manual 中所述,关于内联类型/结构成员:

    不支持大小未知的数组。

    但是,可以使用结构的地址来计算数组数据的偏移量(就像在 C 中一样),然后使用 unsafe_wrap 访问该数据。


    这是一个完整的例子:

    test.c

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct {
        int x;
        int y;
        int z;
    } contained;
    
    typedef struct {
        int n;
        contained arr[];
    } mystruct;
    
    mystruct *return_mystruct() {
        mystruct* ms = malloc(sizeof(mystruct) + 6*sizeof(contained));
    
        ms->n = 6;
    
        for (int i=0; i < 6; i++) {
            printf("i: %d\n", i);
            ms->arr[i].x = i+1;
            ms->arr[i].y = i+1;
            ms->arr[i].z = i+1;
        };
    
        return ms;
    }
    

    编译:gcc -shared -o test test.c

    朱莉娅:

    immutable contained
      x::Cint
      y::Cint
      z::Cint
    end
    immutable mystruct
      n::Cint
    end
    
    ms = ccall((:return_mystruct, "test"), Ptr{mystruct}, ())
    
    n = unsafe_load(ms).n
    addr = reinterpret(Ptr{contained}, ms+sizeof(mystruct))
    arr = unsafe_wrap(Vector{contained}, addr, n)
    

    注意事项:

    • type -> immutable 以确保布局与 C 兼容(参见手册)
    • 请注意,地址必须在任何 Julia 对内存的引用的生命周期内有效,这通常意味着它必须是堆分配的)

    【讨论】:

    • 您所描述的导致段错误的情况实际上是 not 段错误的情况。当 C 结构用contained* arr 定义但使用contained arr[1]contained arr[] 定义段错误时,以下代码可以工作并给出预期的输出:ms = ccall(:mkmystruct, Ptr{mystruct}, ...); m = unsafe_load(ms); n = m.n; arr = [unsafe_load(m.arr,i) for i in 1:n];但是,arr = unsafe_wrap(Array, m.arr, m.n)arr = unsafe_wrap(Array, ms + sizeof(Cint), m.n) 都会导致段错误。
    • 我加了一个例子。
    • 感谢您提供完整的示例。就 Julia 而言,这里 mystruct 不拥有 arr。是否可以保证 arr 所在的内存在另一个 ccall 中的 freed 之前不会被其他内容覆盖?
    • 查看unsafe_wrap 的文档:有一个可选的own 参数,当引用被垃圾回收时,它会告诉Julia free 内存。默认情况下,own=false,因此 Julia 不会触及内存(除了对 unsafe_wrap'd 数组引用所做的任何分配)。使用own=true 是高级用法——在这种情况下永远不应该这样做,因为结构的前四个字节会泄漏。所有其他保证都是您的责任,就像在 C 中一样。“struct hack”有些高级,因此请谨慎使用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-04
    • 2017-10-05
    • 1970-01-01
    • 2012-12-22
    • 2012-07-23
    • 2021-03-12
    相关资源
    最近更新 更多