可互操作派生类型中的 Fortran 固定大小多维数组
Fortran fixed-size multidimensional arrays in interoperable derived types
我有一个 Fortran 库,我正在尝试为其创建 C 绑定。 Fortran 库使用包含在派生类型中的固定大小的多维数组。 (这些最初是遗留 Fortran 代码中的全局变量;出于封装的目的,我将所有全局变量放入派生类型中。)如果我使用此库在 Fortran 中创建测试用例,在这种情况下,派生类型将被初始化在 Fortran 代码中,一切正常,但当我在 C 中尝试相同时,在这种情况下派生类型在 C 中初始化为结构,我得到一个分段错误。
这是一个显示问题的最小示例。 Fortran 库还使用包含其他派生类型的分组派生类型,因此我将其包含在示例中。
testmod.f90:
module testmod
use iso_c_binding
implicit none
integer(c_int), parameter :: RSIZE1 = 360
integer(c_int), parameter :: RSIZE2 = RSIZE1/2
integer(c_int), parameter :: ISIZE1 = RSIZE1
integer(c_int), parameter :: ISIZE2 = ISIZE1/4
type, bind(c) :: struct_a
real(c_double) :: rarray(RSIZE1,RSIZE2)
integer(c_int) :: iarray(ISIZE1,ISIZE2)
end type struct_a
type, bind(c) :: struct_b
real(c_double) :: rvec(RSIZE1)
integer(c_int) :: ivec(ISIZE1)
end type struct_b
type, bind(c) :: struct_group
type(struct_a) :: a
type(struct_b) :: b
end type struct_group
contains
subroutine set_structs(group) bind(c, name="set_structs")
type(struct_group), intent(inout) :: group
integer i, j
do i = 1, RSIZE1
group%b%rvec(i) = dble(i)
group%b%ivec(i) = i
do j = 1, RSIZE2
group%a%rarray(i,j) = dble(i*j)
group%a%iarray(i,j) = i*j
write(*,*) "Here", i, j
end do
end do
end subroutine set_structs
end module testmod
test.h:
#pragma once
#define RSIZE1 360
#define RSIZE2 RSIZE1/2
#define ISIZE1 RSIZE1
#define ISIZE2 ISIZE1/4
typedef struct
{
double rarray[RSIZE1*RSIZE2];
int iarray[ISIZE1*ISIZE2];
} struct_a;
typedef struct
{
double rvec[RSIZE1];
int ivec[ISIZE1];
} struct_b;
typedef struct
{
struct_a a;
struct_b b;
} struct_group;
extern void set_structs(struct_group *group);
test.c:
#include "test.h"
int main()
{
struct_group group;
set_structs(&group);
return 0;
}
编译如下:
gfortran -c -fPIC -Wall testmod.f90
gcc -c -fPIC -Wall test.c
gfortran -o test testmod.o test.o
当我 运行 这样做时,我在 set_structs 中遇到段错误,因为 i = 1,j = 103。但是,如果我注释掉所有对 iarray 的引用,它就可以正常工作。因此,问题似乎仅在 Fortran 派生类型中存在多于 1 个多维数组时才会出现。单个多维数组工作正常(struct_a,注释掉了 iarray),多个一维数组工作正常(struct_b)。我也完全没有派生类型进行了测试,只是将四个数组从 C 传递给 Fortran(两个 2 维和 2 个 1 维),这也能正常工作。我在这里有点不知所措,所以我真的很感激关于如何正确执行此操作的一些建议。
编辑:正如 francescalus 在下面的评论中所指出的,这个例子的问题只是我试图访问 iarray 的越界元素,所以它不是真正问题的一个很好的例子我的代码。请参阅已接受的答案以了解实际原因和解决方案。
我在我的真实代码中找出了问题所在,以防有人疑惑。它似乎与头文件中的#defines 一起。例如,我有如下语句:
#define IQX 360
#define IWX IQX/8+2
#define IZX IQX+IWX
显然这是行不通的,如果我对 C 预处理器更熟悉的话,我可能就知道了。相反,这样做:
#define IQX 360
#define IWX 47
#define IZX 407
我有一个 Fortran 库,我正在尝试为其创建 C 绑定。 Fortran 库使用包含在派生类型中的固定大小的多维数组。 (这些最初是遗留 Fortran 代码中的全局变量;出于封装的目的,我将所有全局变量放入派生类型中。)如果我使用此库在 Fortran 中创建测试用例,在这种情况下,派生类型将被初始化在 Fortran 代码中,一切正常,但当我在 C 中尝试相同时,在这种情况下派生类型在 C 中初始化为结构,我得到一个分段错误。
这是一个显示问题的最小示例。 Fortran 库还使用包含其他派生类型的分组派生类型,因此我将其包含在示例中。
testmod.f90:
module testmod
use iso_c_binding
implicit none
integer(c_int), parameter :: RSIZE1 = 360
integer(c_int), parameter :: RSIZE2 = RSIZE1/2
integer(c_int), parameter :: ISIZE1 = RSIZE1
integer(c_int), parameter :: ISIZE2 = ISIZE1/4
type, bind(c) :: struct_a
real(c_double) :: rarray(RSIZE1,RSIZE2)
integer(c_int) :: iarray(ISIZE1,ISIZE2)
end type struct_a
type, bind(c) :: struct_b
real(c_double) :: rvec(RSIZE1)
integer(c_int) :: ivec(ISIZE1)
end type struct_b
type, bind(c) :: struct_group
type(struct_a) :: a
type(struct_b) :: b
end type struct_group
contains
subroutine set_structs(group) bind(c, name="set_structs")
type(struct_group), intent(inout) :: group
integer i, j
do i = 1, RSIZE1
group%b%rvec(i) = dble(i)
group%b%ivec(i) = i
do j = 1, RSIZE2
group%a%rarray(i,j) = dble(i*j)
group%a%iarray(i,j) = i*j
write(*,*) "Here", i, j
end do
end do
end subroutine set_structs
end module testmod
test.h:
#pragma once
#define RSIZE1 360
#define RSIZE2 RSIZE1/2
#define ISIZE1 RSIZE1
#define ISIZE2 ISIZE1/4
typedef struct
{
double rarray[RSIZE1*RSIZE2];
int iarray[ISIZE1*ISIZE2];
} struct_a;
typedef struct
{
double rvec[RSIZE1];
int ivec[ISIZE1];
} struct_b;
typedef struct
{
struct_a a;
struct_b b;
} struct_group;
extern void set_structs(struct_group *group);
test.c:
#include "test.h"
int main()
{
struct_group group;
set_structs(&group);
return 0;
}
编译如下:
gfortran -c -fPIC -Wall testmod.f90
gcc -c -fPIC -Wall test.c
gfortran -o test testmod.o test.o
当我 运行 这样做时,我在 set_structs 中遇到段错误,因为 i = 1,j = 103。但是,如果我注释掉所有对 iarray 的引用,它就可以正常工作。因此,问题似乎仅在 Fortran 派生类型中存在多于 1 个多维数组时才会出现。单个多维数组工作正常(struct_a,注释掉了 iarray),多个一维数组工作正常(struct_b)。我也完全没有派生类型进行了测试,只是将四个数组从 C 传递给 Fortran(两个 2 维和 2 个 1 维),这也能正常工作。我在这里有点不知所措,所以我真的很感激关于如何正确执行此操作的一些建议。
编辑:正如 francescalus 在下面的评论中所指出的,这个例子的问题只是我试图访问 iarray 的越界元素,所以它不是真正问题的一个很好的例子我的代码。请参阅已接受的答案以了解实际原因和解决方案。
我在我的真实代码中找出了问题所在,以防有人疑惑。它似乎与头文件中的#defines 一起。例如,我有如下语句:
#define IQX 360
#define IWX IQX/8+2
#define IZX IQX+IWX
显然这是行不通的,如果我对 C 预处理器更熟悉的话,我可能就知道了。相反,这样做:
#define IQX 360
#define IWX 47
#define IZX 407