如何判断 Fortran 数组指针是直接分配的还是与另一个对象关联的?
How can I tell if a fortran array pointer has been allocated directly, or is associated with another object?
我正在使用包含一些数组指针的 Fortran 代码;根据用户输入,它们可能被设置为使用赋值语句 =>
指向另一个数组,或者它们可能直接使用 allocate
语句分配。
这会在代码末尾产生释放内存的问题,因为在第一种情况下我只想 nullify
指针,而在第二种情况下我需要 deallocate
它以防止内存泄漏。问题是在这两种情况下 associated
returns true,所以我不知道如何在不手动跟踪的情况下判断我处于哪种情况.由于有很多这样的数组指针,我宁愿避免这样做。
有没有简单的方法来区分这两种情况?
谢谢!
这听起来一团糟。我假设你有这样的东西:
program really_bad
implicit none
integer, dimension(:), pointer :: a
integer, dimension(:), allocatable, target :: b
logical :: point_to_b
allocate(b(10))
write(*, *) "Should I point to B?"
read(*, *) point_to_b
if (point_to_b) then
a => b
else
allocate(a(10))
end if
! Destroy A
end program really_bad
而你的问题是破坏性的部分。如果 a
指向 b
,那么你想要 NULLIFY
它,因为你需要保持 b
。但是如果 a
没有指向 b
,而你只 NULLIFY
它,你就会发生内存泄漏。
正如@ian-bush 指出的那样,您可以检查某个东西是否与其他东西相关联,但这意味着您必须将指针与所有可能的目标进行比较,并且您声称您有很多这样的目标。
你可以尝试一件事,但这可能是一个更糟糕的想法,就是尝试解除分配,并使用它的 STAT=
虚拟参数来检查解除分配是否真的有效。请注意,这是一个可怕的 hack,我只让它在 Intel 上工作,而不是在 GFortran 上工作,但这里什么也没有:
program don
implicit none
integer, dimension(:), pointer :: a
integer, dimension(:), target, allocatable :: b
logical :: point_to_b
integer :: stat
allocate(b(10))
b = (/ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /)
write(*, *) "Should I point to B?"
read(*, *) point_to_b
if ( point_to_b ) then
a => b
else
allocate(a(10))
end if
deallocate(a, stat=stat)
if ( stat /= 0 ) nullify(a)
end program don
一个更好的方法可能是用既有指针又有关联标志的类型替换数组指针。
program don
implicit none
type :: my_array_pointer_type
integer, dimension(:), pointer :: array
logical :: master
end type my_array_pointer_type
type(my_array_pointer_type) :: a
integer, dimension(:), target, allocatable :: b
logical :: point_to_b
integer :: stat
allocate(b(10))
b = (/ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /)
write(*, *) "Should I point to B?"
read(*, *) point_to_b
if ( point_to_b ) then
a%master = .false.
a%array => b
else
a%master = .true.
allocate(a%array(10))
end if
if (a%master) then
deallocate(a%array)
else
nullify(a%array)
end if
end program don
如果您可能随后将另一个指针指向 a
然后尝试销毁它,那么这些建议都不会帮助您。在那种情况下,我真的建议重新考虑您的整个项目设计。
你的数据结构有多复杂?也就是说,在给定时间有多少指针同时关联到单个目标? Fortran 标准在对通过 allocate
语句实例化的目标进行引用计数时完全保持沉默,即它没有指定一种机制来确保在所有指针对象都解除关联时释放目标内存。内置垃圾回收正是 allocatable
属性在 99.9% 的情况下优于 pointer
属性的原因。在极少数情况下,您绝对必须使用指针并且其目标分配在其他某个子程序中,然后将调用链向上传递到不同的过程,跟踪指针所有权将成为一场噩梦。
如果您可以使用现代 Fortran 编译器 (2008+),以下模块可能会有所帮助;仅供参考,模块中声明的变量自动继承 save
属性。目标 foo
是一个私有模块变量,ref_counter
跟踪与 foo
关联的指针数量。通过重载 deallocate
语句,我们可以安全地取消或取消分配。 call deallocate(some_ptr, dealloc_stat)
module foo_target
! Explicit typing only
implicit none
private
public :: deallocate, nullify, point_at_foo
! Neither foo nor ref_counter are public
real, pointer :: foo(:) => null()
integer, protected :: ref_counter = 0
interface deallocate
module procedure deallocate_ptr
end interface deallocate
interface nullify
module procedure nullify_ptr
end interface nullify
contains
subroutine deallocate_ptr(ptr_to_foo, return_stat)
! Dummy arguments
real, pointer, intent (in out) :: ptr_to_foo(:)
integer, intent (out) :: return_stat
! Local variables
enum, bind(C)
enumerator :: DEALLOC_ERROR=1, NO_ASSOC, BAD_ASSOC
end enum
integer :: alloc_stat
! Initialize
return_stat = 0
block_construct: block
! Check association
if (ref_counter == 0 .or. .not. associated(foo)) then
return_stat = NO_ASSOC
exit block_construct
else if (.not. associated(ptr_to_foo, foo)) then
return_stat = BAD_ASSOC
exit block_construct
end if
if (ref_counter > 1 ) then
! Further associations still persist, only nullify
call nullify(ptr_to_foo)
else if (ref_counter == 1) then
! No remaining associations, nullify and deallocate
call nullify(ptr_to_foo)
deallocate(foo, stat=alloc_stat)
! Check deallocation status
if (alloc_stat /= 0) return_stat = DEALLOC_ERROR
end if
end block block_construct
end subroutine deallocate_ptr
subroutine nullify_ptr(ptr_to_foo)
! Dummy arguments
real, pointer, intent (in out) :: ptr_to_foo(:)
! Terminate association
nullify(ptr_to_foo)
! Decrement
ref_counter = ref_counter - 1
end subroutine nullify_ptr
function point_at_foo() result (return_value)
! Dummy arguments
real, pointer :: return_value(:)
! Establish association
return_value => foo
! Increment
ref_counter = ref_counter + 1
end function point_at_foo
end module foo_target
也许对某人有用。我推到了 GitHub 我的引用计数的简单实现,它使您能够跟踪对变量的引用。当引用数为 0 时,对象将自动释放。 Linkhttps://github.com/LadaF/Fortran-RefCount
它基于论文 Car - A reference counting implementation in Fortran 95/2003 and book Scientific Software Design: The Object-Oriented Way 中的想法。
引用对象本身存储指向另一个对象的指针refptr
,后者存储指向数据本身的指针和指向它的引用数。赋值过载,以便更新引用计数。引用对象的终结会根据需要从引用计数中减去。
type refptr
integer :: ref_count = 0
class(*), allocatable :: data
contains
final :: refptr_finalize
end type
type :: ref
type(refptr),pointer :: ptr => null()
integer :: weak = 0 !1.. this is a weak reference, -1.. make weak references to this
contains
procedure :: assign_star
generic :: assignment(=) => assign_star
...
procedure :: pointer => ref_pointer
final :: ref_finalize
end type
interface ref
module procedure ref_init
module procedure ref_init_1
end interface
用法就很简单了:
type(ref) :: a, b
a = 42
print *, "a:", a%value(0)
!this does not copy the value, it points the reference
b = a
print *, "b:", b%value(0)
!sets a to point to something new, b still points to 42
a = 2.3
免责声明:自动终结刚刚实现,没有测试太多。到目前为止,我已经将它与手动调用一起使用以在 gfortran-4.8 中完成。这已经过相当广泛的测试。
我正在使用包含一些数组指针的 Fortran 代码;根据用户输入,它们可能被设置为使用赋值语句 =>
指向另一个数组,或者它们可能直接使用 allocate
语句分配。
这会在代码末尾产生释放内存的问题,因为在第一种情况下我只想 nullify
指针,而在第二种情况下我需要 deallocate
它以防止内存泄漏。问题是在这两种情况下 associated
returns true,所以我不知道如何在不手动跟踪的情况下判断我处于哪种情况.由于有很多这样的数组指针,我宁愿避免这样做。
有没有简单的方法来区分这两种情况?
谢谢!
这听起来一团糟。我假设你有这样的东西:
program really_bad
implicit none
integer, dimension(:), pointer :: a
integer, dimension(:), allocatable, target :: b
logical :: point_to_b
allocate(b(10))
write(*, *) "Should I point to B?"
read(*, *) point_to_b
if (point_to_b) then
a => b
else
allocate(a(10))
end if
! Destroy A
end program really_bad
而你的问题是破坏性的部分。如果 a
指向 b
,那么你想要 NULLIFY
它,因为你需要保持 b
。但是如果 a
没有指向 b
,而你只 NULLIFY
它,你就会发生内存泄漏。
正如@ian-bush 指出的那样,您可以检查某个东西是否与其他东西相关联,但这意味着您必须将指针与所有可能的目标进行比较,并且您声称您有很多这样的目标。
你可以尝试一件事,但这可能是一个更糟糕的想法,就是尝试解除分配,并使用它的 STAT=
虚拟参数来检查解除分配是否真的有效。请注意,这是一个可怕的 hack,我只让它在 Intel 上工作,而不是在 GFortran 上工作,但这里什么也没有:
program don
implicit none
integer, dimension(:), pointer :: a
integer, dimension(:), target, allocatable :: b
logical :: point_to_b
integer :: stat
allocate(b(10))
b = (/ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /)
write(*, *) "Should I point to B?"
read(*, *) point_to_b
if ( point_to_b ) then
a => b
else
allocate(a(10))
end if
deallocate(a, stat=stat)
if ( stat /= 0 ) nullify(a)
end program don
一个更好的方法可能是用既有指针又有关联标志的类型替换数组指针。
program don
implicit none
type :: my_array_pointer_type
integer, dimension(:), pointer :: array
logical :: master
end type my_array_pointer_type
type(my_array_pointer_type) :: a
integer, dimension(:), target, allocatable :: b
logical :: point_to_b
integer :: stat
allocate(b(10))
b = (/ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /)
write(*, *) "Should I point to B?"
read(*, *) point_to_b
if ( point_to_b ) then
a%master = .false.
a%array => b
else
a%master = .true.
allocate(a%array(10))
end if
if (a%master) then
deallocate(a%array)
else
nullify(a%array)
end if
end program don
如果您可能随后将另一个指针指向 a
然后尝试销毁它,那么这些建议都不会帮助您。在那种情况下,我真的建议重新考虑您的整个项目设计。
你的数据结构有多复杂?也就是说,在给定时间有多少指针同时关联到单个目标? Fortran 标准在对通过 allocate
语句实例化的目标进行引用计数时完全保持沉默,即它没有指定一种机制来确保在所有指针对象都解除关联时释放目标内存。内置垃圾回收正是 allocatable
属性在 99.9% 的情况下优于 pointer
属性的原因。在极少数情况下,您绝对必须使用指针并且其目标分配在其他某个子程序中,然后将调用链向上传递到不同的过程,跟踪指针所有权将成为一场噩梦。
如果您可以使用现代 Fortran 编译器 (2008+),以下模块可能会有所帮助;仅供参考,模块中声明的变量自动继承 save
属性。目标 foo
是一个私有模块变量,ref_counter
跟踪与 foo
关联的指针数量。通过重载 deallocate
语句,我们可以安全地取消或取消分配。 call deallocate(some_ptr, dealloc_stat)
module foo_target
! Explicit typing only
implicit none
private
public :: deallocate, nullify, point_at_foo
! Neither foo nor ref_counter are public
real, pointer :: foo(:) => null()
integer, protected :: ref_counter = 0
interface deallocate
module procedure deallocate_ptr
end interface deallocate
interface nullify
module procedure nullify_ptr
end interface nullify
contains
subroutine deallocate_ptr(ptr_to_foo, return_stat)
! Dummy arguments
real, pointer, intent (in out) :: ptr_to_foo(:)
integer, intent (out) :: return_stat
! Local variables
enum, bind(C)
enumerator :: DEALLOC_ERROR=1, NO_ASSOC, BAD_ASSOC
end enum
integer :: alloc_stat
! Initialize
return_stat = 0
block_construct: block
! Check association
if (ref_counter == 0 .or. .not. associated(foo)) then
return_stat = NO_ASSOC
exit block_construct
else if (.not. associated(ptr_to_foo, foo)) then
return_stat = BAD_ASSOC
exit block_construct
end if
if (ref_counter > 1 ) then
! Further associations still persist, only nullify
call nullify(ptr_to_foo)
else if (ref_counter == 1) then
! No remaining associations, nullify and deallocate
call nullify(ptr_to_foo)
deallocate(foo, stat=alloc_stat)
! Check deallocation status
if (alloc_stat /= 0) return_stat = DEALLOC_ERROR
end if
end block block_construct
end subroutine deallocate_ptr
subroutine nullify_ptr(ptr_to_foo)
! Dummy arguments
real, pointer, intent (in out) :: ptr_to_foo(:)
! Terminate association
nullify(ptr_to_foo)
! Decrement
ref_counter = ref_counter - 1
end subroutine nullify_ptr
function point_at_foo() result (return_value)
! Dummy arguments
real, pointer :: return_value(:)
! Establish association
return_value => foo
! Increment
ref_counter = ref_counter + 1
end function point_at_foo
end module foo_target
也许对某人有用。我推到了 GitHub 我的引用计数的简单实现,它使您能够跟踪对变量的引用。当引用数为 0 时,对象将自动释放。 Linkhttps://github.com/LadaF/Fortran-RefCount
它基于论文 Car - A reference counting implementation in Fortran 95/2003 and book Scientific Software Design: The Object-Oriented Way 中的想法。
引用对象本身存储指向另一个对象的指针refptr
,后者存储指向数据本身的指针和指向它的引用数。赋值过载,以便更新引用计数。引用对象的终结会根据需要从引用计数中减去。
type refptr
integer :: ref_count = 0
class(*), allocatable :: data
contains
final :: refptr_finalize
end type
type :: ref
type(refptr),pointer :: ptr => null()
integer :: weak = 0 !1.. this is a weak reference, -1.. make weak references to this
contains
procedure :: assign_star
generic :: assignment(=) => assign_star
...
procedure :: pointer => ref_pointer
final :: ref_finalize
end type
interface ref
module procedure ref_init
module procedure ref_init_1
end interface
用法就很简单了:
type(ref) :: a, b
a = 42
print *, "a:", a%value(0)
!this does not copy the value, it points the reference
b = a
print *, "b:", b%value(0)
!sets a to point to something new, b still points to 42
a = 2.3
免责声明:自动终结刚刚实现,没有测试太多。到目前为止,我已经将它与手动调用一起使用以在 gfortran-4.8 中完成。这已经过相当广泛的测试。