更改子例程内的指针时接收内存访问错误
Receiving Memory Access Error when changing a pointer inside a subroutine
我正在使用 Fortran 和 gfortran 4.7.2。我是 Fortran 的新手,正在深入寻找解决我问题的方法。我要使用的程序有很多功能,应该根据给定的条件正确地别名。为此,我想使用指针。
主程序根据模块func_interface
中的接口创建指针。基于我想要别名的函数,我编写了一个子例程,该子例程应该将指针更改为所需的函数。尽管如此,我在尝试 运行 程序时收到 'Memory Access Error' - 显然是因为我不理解 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
francescalus 和 Vladimir 的评论正是您所需要的。下面我建议对您的代码进行简单的重组,我将所有功能都放在现有模块中。我还评论了 external
语句,因为它们对模块中的函数变得无用。
您会在 S.O 上的许多 fortran
问题上找到以下评论。但值得再次放在这里。开始新项目时,您应该坚持使用现代编程技术。最好将程序放在模块中而不是使用外部。这将自动为您构建界面并在编译时进行一些检查。
现在如果你要使用一些已经存在的函数并且你不修改它们,你需要提供显式接口。
感谢francescalus的评论,我修改了主程序中对selected函数的调用,只在初始化时调用。为避免这种情况,可以在过程 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
给出了解决方案的基本方面:扩展模块包含的内容,以便在主程序中为子例程 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
属性。正如给定的 这样的属性需要在引用子例程的范围内有一个显式接口。另一个答案显示了如何安排(还有许多其他问题和答案可以找到)。
子例程和函数是外部过程,不会自动具有可用的显式接口。
然后你问了
Although I thought that using USE func_interface
is explicitly defining the pointer.. what is the mistake in this thought?
模块 func_interface
包含抽象接口 func
。这个抽象接口用于过程指针的声明。但是,如上所述,子例程 assign_pointer
是有问题的。可以看到伪参数
procedure(), pointer, intent(inout) :: func_ptr
(具有隐式接口)完全独立于模块,但仍然要求子例程的接口在调用范围内是显式的。
所以,抽象接口只是让这个程序工作的一小部分。
甚至那个抽象接口也可能是不必要的。根据 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()
是 允许的,并且由于伪参数具有 intent(inout)
属性,它在进入子例程时保留未关联状态。
我正在使用 Fortran 和 gfortran 4.7.2。我是 Fortran 的新手,正在深入寻找解决我问题的方法。我要使用的程序有很多功能,应该根据给定的条件正确地别名。为此,我想使用指针。
主程序根据模块func_interface
中的接口创建指针。基于我想要别名的函数,我编写了一个子例程,该子例程应该将指针更改为所需的函数。尽管如此,我在尝试 运行 程序时收到 'Memory Access Error' - 显然是因为我不理解 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
francescalus 和 Vladimir 的评论正是您所需要的。下面我建议对您的代码进行简单的重组,我将所有功能都放在现有模块中。我还评论了 external
语句,因为它们对模块中的函数变得无用。
您会在 S.O 上的许多 fortran
问题上找到以下评论。但值得再次放在这里。开始新项目时,您应该坚持使用现代编程技术。最好将程序放在模块中而不是使用外部。这将自动为您构建界面并在编译时进行一些检查。
现在如果你要使用一些已经存在的函数并且你不修改它们,你需要提供显式接口。
感谢francescalus的评论,我修改了主程序中对selected函数的调用,只在初始化时调用。为避免这种情况,可以在过程 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
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
属性。正如给定的
子例程和函数是外部过程,不会自动具有可用的显式接口。
然后你问了
Although I thought that using
USE func_interface
is explicitly defining the pointer.. what is the mistake in this thought?
模块 func_interface
包含抽象接口 func
。这个抽象接口用于过程指针的声明。但是,如上所述,子例程 assign_pointer
是有问题的。可以看到伪参数
procedure(), pointer, intent(inout) :: func_ptr
(具有隐式接口)完全独立于模块,但仍然要求子例程的接口在调用范围内是显式的。
所以,抽象接口只是让这个程序工作的一小部分。
甚至那个抽象接口也可能是不必要的。根据 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()
是 允许的,并且由于伪参数具有 intent(inout)
属性,它在进入子例程时保留未关联状态。