Как передать распределяемые массивы подпрограмм в Fortran

Следующий код возвращает ошибку сегментации, потому что выделяемый массив, который я пытаюсь передать, не распознается должным образом (размер возвращает 1, когда он должен быть 3). На этой странице (http://www.eng-tips.com/viewthread.cfm?qid=170599) аналогичный пример, похоже, указывает на то, что он должен отлично работать в F95; мой файл кода имеет расширение.F90, но я попытался изменить его на F95, и я использую gfortran для компиляции.

Я предполагаю, что проблема должна быть в том, как я передаю распределяемый массив подпрограмме; Что я делаю не так?

!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%!
 PROGRAM test
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%!
 IMPLICIT NONE
 DOUBLE PRECISION,ALLOCATABLE :: Array(:,:)
 INTEGER                      :: iii,jjj

 ALLOCATE(Array(3,3))
 DO iii=1,3
 DO jjj=1,3
    Array(iii,jjj)=iii+jjj
    PRINT*,Array(iii,jjj)
 ENDDO
 ENDDO
 CALL Subtest(Array)

 END PROGRAM
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%!
 SUBROUTINE Subtest(Array)
 DOUBLE PRECISION,ALLOCATABLE,INTENT(IN) :: Array(:,:)
 INTEGER                                 :: iii,jjj

 PRINT*,SIZE(Array,1),SIZE(Array,2)
 DO iii=1,SIZE(Array,1)
 DO jjj=1,SIZE(Array,2)
    PRINT*,Array(iii,jjj)
 ENDDO
 ENDDO

 END SUBROUTINE
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%!

Ответ 1

Если у процедуры есть фиктивный аргумент, который является allocatable, то явный интерфейс требуется в любой области вызова.

(Существует множество вещей, требующих явного интерфейса, а также выделяемый манекен.)

Вы можете предоставить этот явный интерфейс самостоятельно, поместив блок интерфейса для своей подпрограммы внутри основной программы. Альтернативный и далекий, гораздо лучший вариант заключается в том, чтобы поместить подпрограмму внутри модуля, а затем использовать этот модуль в основной программе - затем автоматически создается явный интерфейс. Вот пример этого на сайте eng-tips, на который вы указали ссылку - см. Сообщение от xwb.

Обратите внимание, что для фиктивного аргумента имеет смысл иметь атрибут allocatable, если вы собираетесь сделать что-то, связанное с его статусом распределения - запросите его статус, перераспределите его, освободите его и т.д.

Ответ 2

Также обратите внимание, что ваш выделяемый array фиктивных аргументов объявляется с intent(in), что означает, что его статус распределения будет принадлежать соответствующему фактическому аргументу (и он не может быть изменен во время процедуры). Фактический аргумент, переданный вашей подпрограмме, может быть нераспределенным и, следовательно, незаконным для ссылки, даже с явным интерфейсом. Компилятор не будет знать этого, и поведение запросов, таких как size не определено в таких случаях.

Следовательно, сначала вам нужно проверить статус распределения array с allocated(array) прежде чем ссылаться на его содержимое. Я бы предложил предложить реализовать циклы по всему массиву с lbound и ubound, поскольку в целом вы не можете быть уверены в границах array:

subroutine subtest(array)
  double precision, allocatable, intent(in) :: array(:,:)
  integer                                   :: iii, jjj

  if(allocated(array)) then
    print*, size(array, 1), size(array, 2)
    do iii = lbound(array, 1), ubound(array, 1)
      do jjj = lbound(array, 2), ubound(array, 2)
        print*, array(iii,jjj)
      enddo
    enddo
  endif  
end subroutine

Ответ 3

Это простой пример, который использует атрибуты фиктивного аргумента с модулем.

module arrayMod   
  real,dimension(:,:),allocatable :: theArray    
end module arrayMod

program test
   use arrayMod
   implicit none

   interface
      subroutine arraySub
      end subroutine arraySub
   end interface

   write(*,*) allocated(theArray)
   call arraySub
   write(*,*) allocated(theArray) 
end program test

subroutine arraySub
   use arrayMod

   write(*,*) 'Inside arraySub()'
   allocate(theArray(3,2))
end subroutine arraySub