我正在使用Fortran和gfortran 4.7.2。我对Fortran很陌生并且密切搜索我的问题的解决方案。我想要使用的程序有很多功能,应该根据给定的条件正确别名。为此,我想使用指针。
主程序根据模块func_interface
中的接口创建指针。基于我想要别名的函数,我编写了一个子程序,它应该将指针更改为所需的函数。然而,在尝试运行程序时,我收到“内存访问错误” - 显然是因为我不理解Fortran中的指针或如何将它们传递给子程序以便在子程序中正确地更改它们。
有人知道如何更改程序以便以这种方式使用它吗?该计划如下。
MODULE func_interface
ABSTRACT INTERFACE
FUNCTION func(z)
DOUBLE PRECISION func
DOUBLE PRECISION, INTENT (IN) :: z
END FUNCTION func
END INTERFACE
END MODULE func_interface
SUBROUTINE assign_pointer(i, func_ptr)
USE func_interface
IMPLICIT NONE
PROCEDURE (func), POINTER, INTENT(INOUT) :: func_ptr => NULL ()
INTEGER, INTENT (IN) :: i
DOUBLE PRECISION f1, f2
EXTERNAL f1, f2
SELECT CASE ( i )
CASE ( 1 )
func_ptr => f1
RETURN
CASE ( 2 )
func_ptr => f2
RETURN
END SELECT
END SUBROUTINE assign_pointer
DOUBLE PRECISION FUNCTION f1(x)
IMPLICIT NONE
DOUBLE PRECISION, INTENT(IN) :: x
f1 = 2*x
END FUNCTION f1
DOUBLE PRECISION FUNCTION f2(x)
IMPLICIT NONE
DOUBLE PRECISION, INTENT(IN) :: x
f2 = 4*x
END FUNCTION f2
PROGRAM pointer_test
USE func_interface
IMPLICIT NONE
DOUBLE PRECISION f1, f2
EXTERNAL f1, f2
PROCEDURE (func), POINTER :: func_ptr => NULL ()
CALL assign_pointer( 1, func_ptr )
WRITE(*, '(1PE12.4)') func_ptr(5.2D1)
END PROGRAM pointer_test
错误讯息:
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
Backtrace for this error:
#0 0x7F32AFB92667
#1 0x7F32AFB92C34
#2 0x7F32AF14F19F
#3 0x4007CE in assign_pointer_
#4 0x40085B in MAIN__ at pointer_test.f90:0
Speicherzugriffsfehler
答案 0 :(得分:1)
answer by innoSPG给出了解决方案的基本方面:扩展模块包含的内容,以便在子程序assign_pointer
的主程序中提供显式接口。我将提供更多细节并解决评论中提出的难度。
首先,看一下(简化的)子程序定义:
subroutine assign_pointer(i, func_ptr)
use func_interface ! func is given in here
procedure(func), pointer, intent(inout) :: func_ptr
integer, intent(in) :: i
end subroutine assign_pointer
此子例程的伪参数func_ptr
具有pointer
属性。由于给定elsewhere这样的属性需要引用子例程的作用域中的显式接口。其他答案显示了如何安排(并且还有许多其他问题和答案可以找到)。
子例程和函数是外部过程,不会自动显示可用的接口。
然后你问了
虽然我认为使用
USE func_interface
明确定义指针..这个想法中的错误是什么?
模块func_interface
包含抽象接口func
。此抽象接口用于过程指针的声明。但是,如上所述,它是子例程assign_pointer
,这是有问题的。人们可以看到虚拟参数
procedure(), pointer, intent(inout) :: func_ptr
(具有隐式接口)完全独立于模块,但仍然要求子程序的接口在调用范围内是显式的。
因此,抽象接口只是使该程序工作的一小部分。
甚至那个抽象的界面也许是不必要的。根据{{1}}和f1
的可用方式,我们可以将模块编写为:
f2
也就是说,module full_mod
contains
function f1(..)
end function f1
function f2(..)
end function f2
subroutine assign_pointer(i, func_ptr)
procedure(f1), pointer, intent(inout) :: func_ptr
integer, intent(in) :: i
! f1 and f2 available from the host module
end subroutine assign_pointer
end module
use full_mod
implicit none
procedure(f1), pointer :: func_ptr => NULL()
...
end
和f1
本身可用于提供过程指针的接口,当这些函数在范围内时。
最后一点:虚拟参数f2
可能没有显式初始化。一行如
func_ptr
试图做到这一点。它试图说procedure(func), pointer, intent(inout) :: func_ptr => NULL()
最初被解除了关联。从上面的代码块可以看出func_ptr
应该被移除。应使用标准指针分配
=> NULL()
或者我们可以注意到主程序中的显式初始化
procedure(func), pointer, intent(inout) :: func_ptr
func_ptr => NULL()
是允许的,因为伪参数具有procedure(func), pointer :: func_ptr => NULL()
属性,它在进入子例程时保留了与之无关的状态。
答案 1 :(得分:0)
francescalus和Vladimir的评论是你所需要的。下面我建议对代码进行简单的重组,我将所有函数放在现有模块中。我还评论了external
语句,因为它们对模块中的函数变得无用。
您将在S.O.上的许多fortran
问题上找到以下评论。但值得再次把它放在这里。在开始新项目时,您应该坚持使用现代编程技术。最好将程序放在模块中而不是使用外部程序。这将自动为您构建界面并在编译时进行一些检查。
现在,如果您要使用已存在的某些功能而您没有修改它们,则需要提供显式接口。
感谢francescalus评论,我修改了对主程序中所选功能的调用,只有在初始化时才调用。为避免这种情况,可以在过程assign_pointer
中处理默认情况。
MODULE func_interface
ABSTRACT INTERFACE
FUNCTION func(z)
DOUBLE PRECISION func
DOUBLE PRECISION, INTENT (IN) :: z
END FUNCTION func
END INTERFACE
CONTAINS
SUBROUTINE assign_pointer(i, func_ptr)
! USE func_interface
IMPLICIT NONE
PROCEDURE (func), POINTER, INTENT(INOUT) :: func_ptr => NULL ()
INTEGER, INTENT (IN) :: i
!DOUBLE PRECISION f1, f2
!EXTERNAL f1, f2
SELECT CASE ( i )
CASE ( 1 )
func_ptr => f1
RETURN
CASE ( 2 )
func_ptr => f2
RETURN
END SELECT
END SUBROUTINE assign_pointer
DOUBLE PRECISION FUNCTION f1(x)
IMPLICIT NONE
DOUBLE PRECISION, INTENT(IN) :: x
f1 = 2*x
END FUNCTION f1
DOUBLE PRECISION FUNCTION f2(x)
IMPLICIT NONE
DOUBLE PRECISION, INTENT(IN) :: x
f2 = 4*x
END FUNCTION f2
END MODULE func_interface
PROGRAM pointer_test
USE func_interface
IMPLICIT NONE
!DOUBLE PRECISION f1, f2
!EXTERNAL f1, f2
PROCEDURE (func), POINTER :: func_ptr => NULL ()
CALL assign_pointer( 1, func_ptr )
IF(associated(func_ptr))then
WRITE(*, '(1PE12.4)') func_ptr(5.2D1)
ELSE
! manage the cas
END IF
END PROGRAM pointer_test