我正在使用一个C库,其中一个结构包含另一个结构(不是指针):
typedef struct {
int a;
double b;
} A;
typedef struct {
A a;
A b;
int c;
} B;
示例初始化:
B* mkB() {
A a = {2, 3.0};
A b = {4, 5.0};
B* rv = (B*)malloc(sizeof(B));
rv->a = a;
rv->b = b;
rv->c = 6;
return rv;
}
以下是朱莉娅的相应类型:
type A
a::Cint
b::Cdouble
end
type B
a::A
b::A
c::Cint
end
现在sizeof(A) = 16
,这是有道理的:Cint
为4个字节,填充为4个字节,因此Cdouble
对齐,Cdouble
为8个字节。 / p>
但Julia说sizeof(B) = 24
,fieldoffset
只为字段a
和b
添加8个字节,只有将它们存储为引用而不是值时才有意义:
julia> ofA = [Int(fieldoffset(A, i)) for i in 1:nfields(A)]'
1x2 Array{Int64,2}:
0 8
julia> ofB = [Int(fieldoffset(B, i)) for i in 1:nfields(B)]'
1x2 Array{Int64,2}:
0 8 16
这是一个问题,因为如果不做一些更改,就无法加载从C函数返回的这种结构的指针:
julia> x = A(2, 3.0); y = A(4, 5.0); z = B(x, y, 6);
julia> pz = pointer_from_objref(z); #from Julia
julia> pb = ccall(("mkB", mylib), Ptr{B}, ()); #from C
julia> unsafe_load(reinterpret(Ptr{B}, pz)) #works as expected
B(A(2,3.0),A(4,5.0),6)
julia> unsafe_load(reinterpret(Ptr{B}, pb)) #segfaults
但是,给定C偏移量,可以单独提取每个元素。这里很明显Julia在类型A
中存储类型B
作为指针,而整个A
存储在C中内联:
julia> unsafe_load(reinterpret(Ptr{A}, pb)) #from C
A(2,3.0) #correct
julia> unsafe_load(reinterpret(Ptr{A}, pz)) #from Julia
A(1274099440,6.9455678017566e-310) #incorrect
julia> unsafe_load(unsafe_load(reinterpret(Ptr{Ptr{A}}, pz)))
A(2,3.0) #correct
julia> unsafe_load(reinterpret(Ptr{Cint}, pb+32)) #from C
6 #B.c offset 32 bytes
julia> unsafe_load(reinterpret(Ptr{Cint}, pz+16)) #from Julia
6 #B.c offset 16 bytes
由于如果B是在C中创建的,unsafe_load
上的Ptr{B}
不起作用,我一直在使用显式偏移来构建兼容的Julia类型:
function B(p::Ptr{B}) #inner constructor for B
of = [0, 16, 32] #offsets in C
jB = new()
for i in 1:nfields(B)
v = unsafe_load(reinterpret(Ptr{fieldtype(B,i)}, p + of[i]))
setfield!(jB, fieldname(B, i), v)
end
end
这可以从指向C分配内存的指针构建Julia类型,但是我需要更改一些字段值(在Julia中)并将指针传递回C函数。 pointer_from_objref
不适用于此,因为C期望struct元素作为值,但Julia将它们存储为指针。结构后面的每个成员都有错误的偏移量。
问题:如何获得指向具有与C相同内存布局的数据的指针?有没有办法告诉Julia按值存储B.a
和B.b
?
答案 0 :(得分:4)
而不是type
,将这些声明为immutable
,用于C兼容的内联布局。 immutable B ...
提供sizeof(B) == 40
。
根据manual:
递归使用时,isbits类型以内联方式存储。所有其他类型都存储为指向数据的指针。当镜像在C中的另一个结构体内使用的值的结构时,必须不要尝试手动复制字段,因为这不会保留正确的字段对齐。相反,声明一个不可变的isbits类型并使用它。在Julia的翻译中无法使用未命名的结构。