【发布时间】:2012-08-13 13:04:23
【问题描述】:
我的主要问题是为什么数组会做如此奇怪的事情,以及是否有任何方法可以以“干净”的方式执行以下操作。
我目前有一个 C 程序 foo.c 通过 dlopen/dlsym 连接一个 Fortran 程序 bar.f90,大致类似于下面的代码:
foo.c:
#include <dlfcn.h>
#include <stdio.h>
int main()
{
int i, k = 4;
double arr[k];
char * e;
void * bar = dlopen("Code/Test/bar.so", RTLD_NOW | RTLD_LOCAL);
void (*allocArray)(int*);
*(void **)(&allocArray) = dlsym(bar, "__bar_MOD_allocarray");
void (*fillArray)(double*);
*(void **)(&fillArray) = dlsym(bar, "__bar_MOD_fillarray");
void (*printArray)(void);
*(void **)(&printArray) = dlsym(bar, "__bar_MOD_printarray");
double *a = (double*)dlsym(bar, "__bar_MOD_a");
for(i = 0; i < k; i++)
arr[i] = i * 3.14;
(*allocArray)(&k);
(*fillArray)(arr);
(*printArray)();
for(i = 0; i < 4; i++)
printf("%f ", a[i]);
printf("\n");
return 0;
}
bar.f90:
module bar
integer, parameter :: pa = selected_real_kind(15, 307)
real(pa), dimension(:), allocatable :: a
integer :: as
contains
subroutine allocArray(asize)
integer, intent(in) :: asize
as = asize
allocate(a(asize))
return
end subroutine
subroutine fillArray(values)
real(pa), dimension(as), intent(in) :: values
a = values
return
end subroutine
subroutine printArray()
write(*,*) a
return
end subroutine
end module
运行主产量
0.0000000000000000 3.1400000000000001 6.2800000000000002 9.4199999999999999
0.000000 -nan 0.000000 0.000000
这表明 Fortran 正确地分配了数组,甚至正确地存储了给定的值,但它们不再可以通过 dlsym 访问(处理该数据会导致段错误)。我也对固定大小的数组进行了尝试 - 结果保持不变。
有人知道这种行为的原因吗?就我个人而言,我希望事情要么双向解决,要么根本不解决——这个“Fortran 接受 C 数组,但反之不行”让我想知道我在以这种方式从 C 访问数组时是否犯了一些基本错误。
另一个(甚至更重要的)问题是,如何以“正确的方式”进行数组访问。目前我什至不确定坚持“Fortran as .so”界面是否是一个好方法——我认为在这种情况下也可以尝试混合编程。尽管如此,数组问题仍然存在 - 我读到这可以使用 ISO C 绑定以某种方式解决,但我不知道如何解决(我还没有使用 Fortran 进行很多工作,尤其是没有使用所述绑定) ,因此非常感谢您对此问题的帮助。
编辑:
好的,所以我进一步阅读了 ISO C 绑定,发现了一个非常有用的方法here。使用 C_LOC 我可以获得指向我的 Fortran 结构的 C 指针。不幸的是,指向数组的指针似乎是指向指针的指针,需要在 C 代码中取消引用,然后才能将它们视为 C 数组 - 或类似的东西。
编辑:
让我的程序现在可以按照 Vladimir F 指出的方式使用 C 绑定运行,至少在大多数情况下如此。 C 文件和 Fortran 文件现在链接在一起,因此我可以避免使用 libdl 接口,至少对于 Fortran 部分而言 - 我仍然需要加载动态 C 库,获取指向其中一个符号的函数指针并传递它作为指向 Fortran 的函数指针,后者随后将该函数作为其计算的一部分调用。正如所说的函数需要 double*s [arrays],奇怪的是,我无法使用 C_LOC 传递我的 Fortran 数组 - C_LOC(array) 和 C_LOC(array(1)) 都没有将正确的指针传递回 C 函数。 array(1) 做到了。可悲的是,这不是做到这一点的“最干净”的方式。如果有人提示我如何使用C_LOC 函数来做到这一点,那就太好了。尽管如此,我还是接受 Vladimir F 的回答,因为我认为这是更安全的解决方案。
【问题讨论】:
-
你绝对应该使用 ISO_C_BINDING,这个网站上有很多关于这个的问题和答案
-
您通常不使用 C_LOC 和 C_PTR 传递数组(如果是,则使用 VALUE!),而是直接传递(对于 bind(C) 过程,Fortran 是通过引用传递)。
标签: c fortran dlsym fortran2003 fortran-iso-c-binding