You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过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

补充说明:如果文件中没有字符串部分,仅读取浮点数组时代码运行正常。


解决方案

问题根源

  1. Fortran参数传递不匹配:GET_Char_COL中的numel参数声明为intent(in)但未加value属性,Fortran默认按引用传递该整数,但Python传递的是ctypes.c_int实例(指针),导致栈内存访问异常。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 15:24:51