Python通过ctypes调用Fortran处理对象数组时访问冲突错误的解决咨询
问题修复与代码优化
错误根源
- Python指针传递错误:你错误地传递了数组指针的地址,而非数组的起始地址。
ct.byref(arr_ptr)会创建指向指针的指针,但Fortran的C_F_POINTER期望直接接收指向结构体数组的C指针。 - Fortran内存非法释放:
my_array是通过C_F_POINTER绑定的外部Python内存,并非Fortran自行分配的内存,调用deallocate(my_array)会触发内存访问违规。
修正后的Python代码
import ctypes as ct import os import numpy as np here = os.path.dirname(__file__) lib_file = os.path.abspath(os.path.join(here, '..', 'fortran', 'svector_test_mod.dll')) fortlib = ct.CDLL(lib_file, winmode=0) class StateVector(ct.Structure): _fields_ = [ ('x', ct.c_longdouble), ('y', ct.c_longdouble), ('z', ct.c_longdouble), ('vx', ct.c_longdouble), ('vy', ct.c_longdouble), ('vz', ct.c_longdouble), ('t', ct.c_longdouble), ] # 显式声明Fortran函数参数类型,避免类型不匹配 fortlib.lists_change.argtypes = [ct.POINTER(StateVector), ct.c_int] fortlib.lists_change.restype = None size = 5 array = (StateVector * size)() for i in range(size): array[i].x = ct.c_longdouble(2.5) array[i].y = ct.c_longdouble(2.5) array[i].z = ct.c_longdouble(2.5) array[i].vx = ct.c_longdouble(2.5) array[i].vy = ct.c_longdouble(2.5) array[i].vz = ct.c_longdouble(2.5) array[i].t = ct.c_longdouble(2.5) # 直接获取结构体数组的指针 arr_ptr = ct.cast(array, ct.POINTER(StateVector)) # 也可以用numpy数组方式: # np_objects = np.ctypeslib.as_array(array) # arr_ptr = np_objects.ctypes.data_as(ct.POINTER(StateVector)) fortlib.lists_change(arr_ptr, size) # 验证处理结果 for i in range(size): print(f"第{i+1}个元素: x={array[i].x}, y={array[i].y}, z={array[i].z}, vx={array[i].vx}, vy={array[i].vy}, vz={array[i].vz}, t={array[i].t}")
修正后的Fortran代码
module state_vector_mod use, intrinsic :: iso_c_binding, only: C_LONG_DOUBLE, c_int, C_PTR, C_F_POINTER implicit none type, bind(C) :: state_vector real(C_LONG_DOUBLE) :: x real(C_LONG_DOUBLE) :: y real(C_LONG_DOUBLE) :: z real(C_LONG_DOUBLE) :: vx real(C_LONG_DOUBLE) :: vy real(C_LONG_DOUBLE) :: vz real(C_LONG_DOUBLE) :: t end type state_vector contains subroutine make_sv(x, y, z, vx, vy, vz, t, sv) bind(c, name='make_sv') real(C_LONG_DOUBLE), intent(in) :: x real(C_LONG_DOUBLE), intent(in) :: y real(C_LONG_DOUBLE), intent(in) :: z real(C_LONG_DOUBLE), intent(in) :: vx real(C_LONG_DOUBLE), intent(in) :: vy real(C_LONG_DOUBLE), intent(in) :: vz real(C_LONG_DOUBLE), intent(in) :: t type(state_vector), intent(out), target :: sv sv = state_vector(x, y, z, vx, vy, vz, t) end subroutine make_sv subroutine lists_change(in_list, length) bind(C, name='lists_change') type(C_PTR), intent(in) :: in_list ! 指针本身无需修改,用intent(in) type(state_vector), dimension(:), pointer :: my_array integer(c_int), intent(in) :: length integer(c_int) :: i ! 将C指针绑定为Fortran可访问的数组指针 call C_F_POINTER(in_list, my_array, [length]) DO i = 1, length my_array(i)%x = my_array(i)%x * 2 my_array(i)%y = my_array(i)%y * 2 my_array(i)%z = my_array(i)%z * 2 my_array(i)%vx = my_array(i)%vx * 2 my_array(i)%vy = my_array(i)%vy * 2 my_array(i)%vz = my_array(i)%vz * 2 my_array(i)%t = my_array(i)%t * 2 END DO ! 移除非法的deallocate语句,内存由Python管理 end subroutine lists_change end module state_vector_mod
关键优化说明
- Python端:必须显式声明
argtypes,避免参数类型自动转换引发的内存错误;直接传递结构体数组指针,无需额外包装。 - Fortran端:删除
deallocate(my_array),该数组内存由Python负责;将in_list的intent改为in,因为仅需访问指针指向的内存,无需修改指针本身。
内容的提问来源于stack exchange,提问作者kirkas
相关产品推荐
相关产品推荐

