2016-07-13 1 views
3

일부 배열 포인터가 포함 된 포트란 코드로 작업하고 있습니다. 사용자 입력에 따라 할당 문 =>을 사용하여 다른 배열을 가리 키도록 설정하거나 allocate 문으로 직접 할당 할 수 있습니다.Fortran 배열 포인터가 직접 할당되었거나 다른 객체와 연관되어 있는지 어떻게 알 수 있습니까?

두 번째 경우에는 deallocate 메모리 누수를 방지해야하기 때문에 첫 번째 경우에는 포인터를 nullify으로 지정하기 때문에 코드 끝에 메모리를 확보하는 데 문제가 발생합니다. 문제는 두 경우 모두 associated이 인 을 반환하기 때문에 수동으로 추적하지 않고 어떤 경우에 있는지 알 수있는 방법을 알지 못합니다. 배열 포인터가 많이 있기 때문에 그렇게하지 않아도됩니다.

두 가지 경우를 구분하는 간단한 방법이 있습니까?

감사합니다.

+0

"코드 끝에"얼마나 가까이에 의미합니까? 실행하는 동안 나는 메모리 누출 등을 신경 쓸 수도 있지만, 프로그램이 끝나면 모든 것이 정리되도록하는 것이 더 이상 나의 책임이 아니다. – francescalus

+0

나는 당신이 무슨 뜻인지 이해할 수 없다. 분명히 나는 ​​프로그램이 종료되기 전에 할당 해제 된 모든 메모리를 목표로해야만한다. –

+0

나는 (끔찍한) 프로그램을 생각해 보았다.'allocatable i; allocate (i); 끝 '. 나는 그곳에'할당 해제 '를하지 않은 것에 대한 죄책감을 조금도 느끼지 않는다. 할당 취소에 대한 책임을지고 싶지만 여기에는없는 경우가 있습니다. 당신의 질문의 전제가 'deallocate' 진술이 있어야 하는가? 나의 첫 번째 코멘트는 그것을 확증하는 것이었다. – francescalus

답변

4

이것은 끔찍한 혼란처럼 들립니다. 나는 당신이 다음과 같은 것을 가지고 있다고 가정합니다.

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 

그리고 당신의 문제는 파괴적인 부분입니다. ab을 가리키는 경우 NULLIFY으로 지정해야합니다. b을 유지해야하기 때문입니다. 그러나 ab을 가리키고 있지 않으며 사용자가 NULLIFY 인 경우 메모리 누수가 발생합니다.

@ ian-bush는 어떤 것이 다른 것과 연관되어 있는지 확인할 수는 있지만 가능한 모든 대상과 포인터를 비교해야한다는 것을 의미하며 그 중 많은 수가 있다고 주장 할 수 있습니다.

시도해 볼 수있는 한 가지 방법은 생각보다 나쁘지만 할당을 해제하고 더미 인수를 STAT= 사용하여 할당 해제가 실제로 작동하는지 확인하는 것입니다. 이 끔찍한 해킹주의, 나는 단지 그것을 GFortran에, 인텔에서 작동하지있어, 그러나 여기 간다하시기 바랍니다 아무것도 :

program don 
    implicit none 
    integer, dimension(:), pointer :: a 
    integer, dimension(:), target, allocatable :: b 
    logical :: point_to_b 
    integer :: stat 

    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 또 다른 포인터를 가리킨 다음 파괴 시도 할 수있는 경우 이러한 제안 중 어느 것도 도움이됩니다. 이 경우 전체 프로젝트 설계를 다시 생각해 보라고 제안합니다.

+0

답변에 많은 감사드립니다. 당신은 그 상황을 완전히 이해했습니다. 추가 플래그를 사용하는 마지막 방법이 앞으로 나아갈 수있는 유일한 방법이라고 결론을 내 렸습니다 (다른 점은 포인터를 가리킬 것이므로 아무런 문제가 없습니다). 나는 그것을 피할 수 있기를 바라고 있었지만 실제로는 다른 방법이없는 것처럼 보입니다. –

4

데이터 구조는 얼마나 복잡합니까? 즉, 주어진 시간에 얼마나 많은 포인터가 하나의 타겟에 동시에 연관되어 있습니까? Fortran 표준은 allocate 문을 통해 인스턴스화 된 대상을 참조 카운팅하는 경우 완전히 침묵합니다. 즉, 대상 메모리가 모두 분리 된 경우 대상 메모리가 해제되도록하는 메커니즘을 지정하지 않습니다. 내장 된 가비지 수집은 정확하게 allocatable 속성이 pointer 속성보다 99.9 % 더 선호되는 이유입니다. 아주 드물게 포인터를 반드시 사용해야하고 그 타겟이 다른 서브 프로그램에 할당 된 다음 콜 체인을 다른 프로 시저로 전달하면 포인터 소유권을 추적하는 것이 악몽이됩니다.

최신 Fortran 컴파일러 (2008+)를 사용하는 경우 다음 모듈이 유용 할 수 있습니다. 참고로 모듈에 선언 된 변수는 자동으로 save 속성을 상속합니다. 목표 foo은 개인 모듈 변수이고 ref_counterfoo과 연관된 포인터의 수를 추적합니다. 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 
+1

필자는 비슷한 구조를 사용합니다. 그러나 내 생각에 참조 카운트의 모든 기능은 완전히 새로운 참조 파생 형식을 만들고 할당을 오버로드하고 finalify를 구현할 때만 발생합니다 (nullify_ptr 및 point_at_foo와 비슷한 방식으로). –

2

아마도 누군가에게 유용 할 것입니다. 나는 당신이 변수에 대한 참조를 추적 할 수있게 해주는 참조 카운팅의 간단한 구현을 GitHub로 푸시했다. 참조 번호가 0 일 때 객체는 자동으로 할당이 해제됩니다. 링크 https://github.com/LadaF/Fortran-RefCount

이것은 Car - A reference counting implementation in Fortran 95/2003의 종이와 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에서 마무리하기 위해 수동 호출을 사용했다. 이것은 상당히 광범위하게 테스트되었습니다.

관련 문제