Fortran中将函数名作为参数传递时数组入参失效的问题排查与解决
异常产生的核心原因
问题根源来自过程调用时接口不匹配:
- 在
call_test子例程里通过double precision,external::fn声明形参fn时,并没有给编译器提供fn的完整接口信息,编译器仅知道这是一个返回双精度标量的外部过程,完全不知道它的入参是数组、还是标量、数组的形状约定是什么。 - 实际传入的
ga函数,入参x是假定形状的显式接口数组(声明为double precision,dimension(0:) :: x),这类数组的传递需要附带专门的数组描述符(记录数组秩、各维上下界、起始地址等信息)。按照Fortran语言规则,假定形状数组作为过程入参时,必须为被调过程提供显式接口才能正确传递参数。 - 当你用无接口的
external声明调用ga时,编译器按照老式的隐式接口规则,直接把数组的首地址作为入参传递,完全没有传递ga需要的数组描述符:- 对ifort来说,ga内部读取
size(x)时,会把栈上的随机内存值当成数组长度,最终访问非法内存触发段错误; - 对gfortran来说,会因为参数不匹配默认构造一个空的数组对象,导致数组长度为0、求和结果为0的错误表现。
- 对ifort来说,ga内部读取
- 你之前测试标量入参时能正常运行,是因为标量的隐式传递规则刚好和接口约定对齐,没有触发参数传递的错位问题。
可运行的代码修改方案
修改思路是放弃无接口的external声明,通过抽象接口块给call_test的函数形参定义明确的签名,匹配实际传入的ga的接口要求,修改后的完整可运行代码如下:
module fun implicit none private public :: ga, call_test ! 新增:定义匹配要求的抽象函数接口 abstract interface function scalar_return_vecfun(x) result(res) double precision, intent(in) :: x(:) double precision :: res end function scalar_return_vecfun end interface contains subroutine call_test(fn) ! 修改:用procedure指定接口类型,替换原来的external声明 procedure(scalar_return_vecfun) :: fn double precision :: f double precision, dimension(0:2) :: x0 x0 = 0.111d0 print*,'Input x0=',x0 print*,'sze x0:',size(x0) f = fn(x0) print*,'fn(x0)=',f end subroutine call_test function ga(x) result(f) double precision, dimension(0:) :: x double precision :: f print*,'size x inside ga:',size(x) print*,'x in ga is:=',x f = sum(x) end function ga end module program main use fun call call_test(ga) end program main
修改后不管是ifort还是gfortran编译运行,都会得到正确的执行结果:
Input x0= 0.111000000000000 0.111000000000000 0.111000000000000 sze x0: 3 size x inside ga: 3 x in ga is:= 0.111000000000000 0.111000000000000 0.111000000000000 fn(x0)= 0.333000000000000
补充说明:如果后续需要同时适配标量入参、不同秩数组入参的函数传入,可以在抽象接口块中新增重载的函数签名,通过过程泛化的方式匹配不同的调用场景,不需要再回到老式的无接口external写法。
内容的提问来源于stack exchange,提问作者Kiyomine
相关产品推荐
相关产品推荐

