更改子例程内的指针时接收内存访问错误

时间:2016-09-20 12:20:04

标签: function pointers fortran alias subroutine

我正在使用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

2 个答案:

答案 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