通过ctypes从Fortran DLL向Python传递字符串数组的问题排查
问题:ctypes调用Fortran DLL读取字符串数组时触发栈溢出
我想用ctypes调用Fortran DLL读取文件,文件结构如下:
AX BX … . 4.0 5.0 6.9 1.2 8.0 7.0 … … …
需要把文件路径传给Fortran DLL,从中获取AX、BX这类字符串组成的字符数组,以及数字组成的浮点数组。目前获取浮点数组正常,但获取字符串数组时出现OSError: exception: stack overflow错误。
Python代码
import ctypes import numpy as np from numpy.ctypeslib import ndpointer import os def Import_DLL(): PATH = os.path.dirname(os.path.realpath(__file__)) Lib = ctypes.CDLL(os.path.join(PATH,"./DLL_read.dll")) return Lib def Decla_Arg(): Lib.open_file.argtypes = [ctypes.c_int, ctypes.c_char_p,ctypes.c_int] Lib.open_file.restype = None Lib.get_char_col.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_int), ndpointer(dtype=np.dtype('a16'))] Lib.get_char_col.restype = None Lib.get_float.argtypes = [ctypes.c_int, ctypes.c_int, ndpointer(dtype=ctypes.c_float)] Lib.get_float.restype = None def open_file(FicWxx,PATH): Lib.open_file(FicWxx,PATH.encode("utf-8"),len(PATH)) return def get_char_col(FicWxx,nliais): z=[' ' for i in range(nliais)] elem=np.asarray(z, dtype = np.dtype('a16')) nliais = ctypes.c_int(nliais) Lib.get_char_col(FicWxx, nliais, elem) return elem def get_res_type(FicWxx, nliais): param=np.zeros(shape=(3 , nliais),dtype=ctypes.c_float) nliais = ctypes.c_int(nliais) Lib.get_float(FicWxx, nliais,param) return param # -------------- main -------------------------------- if __name__ == "__main__": Lib=Import_DLL() Decla_Arg() nliais=3 FicWxx = 15 PATH ="......\\test.txt" open_file(FicWxx,PATH) elem = get_char_col(FicWxx, nliais) print("elem =", elem) param = get_res_type(FicWxx, nliais) print("param =", param)
Fortran代码
module read_file USE iso_fortran_env USE,INTRINSIC :: ISO_C_BINDING implicit none contains subroutine OPEN_FILE(FicWxx,path_cptr,lenpath_cptr) BIND(C) !DEC$ ATTRIBUTES DLLEXPORT :: OPEN_FILE integer (KIND=C_INT) , intent(in) ,value :: FicWxx type(c_ptr), value :: path_cptr integer(c_int), value :: lenpath_cptr character(len=lenpath_cptr,kind=c_char), pointer :: PATH logical :: lexist call c_f_pointer(path_cptr, PATH) inquire(file=trim(PATH),exist=lexist) if(.not.lexist) then stop else open(FicWxx,file=trim(PATH)) end if end subroutine OPEN_FILE !--------------------------- read file ---------------------------------------- subroutine GET_Char_COL(FicWxx,numel,Py_Elem ) BIND(C) !DEC$ ATTRIBUTES DLLEXPORT :: GET_Char_COL integer (KIND=C_INT) ,value , intent(in) :: FicWxx integer (KIND=C_INT) , intent(in) :: numel type(c_ptr) , value :: Py_Elem character(KIND=C_char,len=16) ,dimension(:) ,pointer :: Elem integer :: n call c_f_pointer(Py_Elem,Elem) do n = 1, numel read(FicWxx,*)Elem(n) end do end subroutine GET_Char_COL ! subroutine GET_FLOAT(FicWxx,numel,param) BIND(C) !DEC$ ATTRIBUTES DLLEXPORT :: GET_FLOAT integer, parameter :: Ncol=3 integer (KIND=C_INT) ,value , intent(in) :: FicWxx integer (KIND=C_INT) ,value , intent(in) :: numel real (KIND=C_FLOAT),dimension(numel,Ncol), intent(out) :: param integer :: n do n = 1, numel read(FicWxx,*) param(n,:) end do end subroutine GET_FLOAT end module read_file
编译命令(IFORT英特尔编译器)
$ ifort -c main.f90 $ ifort -dll -exe:DLL_read.dll main.obj
错误信息
调用get_char_col函数时触发:OSError: exception: stack overflow
补充说明:如果文件中没有字符串部分,仅读取浮点数组时代码运行正常。
解决方案
问题根源
- Fortran参数传递不匹配:
GET_Char_COL中的numel参数声明为intent(in)但未加value属性,Fortran默认按引用传递该整数,但Python传递的是ctypes.c_int实例(指针),导致栈内存访问异常。 - Python参数类型声明错误:对应
numel的参数类型被错误声明为指针,进一步加剧了参数传递的不匹配。
修复步骤
1. 修改Fortran的GET_Char_COL子程序
给numel参数添加value属性,确保按值传递:
subroutine GET_Char_COL(FicWxx,numel,Py_Elem ) BIND(C) !DEC$ ATTRIBUTES DLLEXPORT :: GET_Char_COL integer (KIND=C_INT) ,value , intent(in) :: FicWxx integer (KIND=C_INT) ,value , intent(in) :: numel ! 新增value属性 type(c_ptr) , value :: Py_Elem character(KIND=C_char,len=16) ,dimension(:) ,pointer :: Elem integer :: n call c_f_pointer(Py_Elem,Elem) do n = 1, numel read(FicWxx,*)Elem(n) end do end subroutine GET_Char_COL
2. 修正Python的参数声明
将Lib.get_char_col.argtypes中的numel类型从指针改为普通整数:
def Decla_Arg(): # ... 其他代码不变 Lib.get_char_col.argtypes = [ctypes.c_int, ctypes.c_int, ndpointer(dtype=np.dtype('a16'))] # ... 其他代码不变
验证修复
重新编译Fortran DLL后运行Python代码,此时可正常读取字符串数组与浮点数组,不会触发栈溢出错误。
内容的提问来源于stack exchange,提问作者didie
相关产品推荐
相关产品推荐

