Эквивалентность между фортрановскими массивами и указателями - PullRequest
4 голосов
/ 20 февраля 2012

У меня есть программа на Фортране с размещаемым массивом A следующим образом:

real, dimension(:,:) allocatable :: A
...
allocate(A(x0:x1;y0:y1))

Этот массив в конечном итоге передается в качестве аргумента подпрограмме, которая выглядит как

subroutine my_subroutine(arr)
   real, dimension(x0:x1,y0:y1) :: arr
   ...
end subroutine my_subroutine

Iхотел заменить оператор allocate на Fortran пользовательской функцией выделения памяти my_alloc, реализованной в библиотеке C.Я изменил первый пример кода на:

type(c_ptr) :: cptr
real, pointer, dimension(:,:) :: A
...
cptr = my_alloc(...)
call c_f_pointer(cptr,A,[x1-x0+1,y1-y0+1])

Это прекрасно работает, за исключением того, что, задавая экстенты вместо нижних / верхних границ в функции c_f_pointer, я теряю исходную форму (x0: x1, y0: y1) массива.Но это не большая проблема: указатель передается в качестве аргумента подпрограммы, подпрограмма ожидает массив и рассматривает указатель как массив с надлежащими границами.

Моя настоящая проблема: когда я хочутакже переписать код подпрограммы, чтобы вместо массива был указатель.

subroutine my_subroutine(arr)
   real, pointer, dimension(x0:x1,y0:y1) :: arr
   ...
end subroutine my_subroutine

Приведенный выше код не работает;gfortran говорит:

Array pointer 'arr' at (1) must have a deferred shape

Следующий код может быть скомпилирован

subroutine my_subroutine(arr)
   real, pointer, dimension(:,:) :: arr
   ...
end subroutine my_subroutine

, но он не предоставляет границ, и программа вылетает, когда я пытаюсь выполнить цикл от x0 до x1 иот y0 до y1.

Как я могу справиться с этим делом?В подпрограмме мне нужно, чтобы Fortran знал, что arr - это указатель на массив в форме (x0: x1, y0; y1).

Ответы [ 3 ]

5 голосов
/ 20 февраля 2012

Да, это была проблема из-за ограничения c_f_pointer.Как вы обнаружили, встроенный c_f_pointer поддерживает только границы, начинающиеся с индекса 1. Люди часто утверждают, что Fortran - это язык с одним индексом, но это не так.Одна индексация используется только по умолчанию, и Fortran давно поддерживает объявление любой начальной границы, которую хочет программист.Так что это был шаг назад, когда c_f_pointer заставил вас использовать одну индексацию.Но в Fortran 2003 есть исправление: переопределение границ указателя:

arr (0:n-1) => arr

вместо 1: n или что угодно.

Затем передайте массив подпрограмме, и он получитпредполагаемые границы.

РЕДАКТИРОВАТЬ: улучшить демонстрационную программу, показывая разницу между назначаемыми и указателями.Указатель переходит границы массива.Обычный массив передает форму ... вы можете объявить первое измерение в подпрограмме, если хотите, и позволить форме управлять вторым.

module mysubs

implicit none

contains

subroutine testsub ( ptr, alloc, start, array )

   real, pointer, dimension (:) :: ptr
   real, dimension (:), intent (in) :: alloc
   integer, intent (in) :: start
   real, dimension (start:), intent (in) :: array

   write (*, *) "pointer in sub:", lbound (ptr, 1), ubound (ptr, 1)
   write (*, *) ptr

   write (*, *) "1st array in sub:", lbound (alloc, 1), ubound (alloc, 1)
   write (*, *) alloc

   write (*, *) "2nd array in sub:", lbound (array, 1), ubound (array, 1)
   write (*, *) array

   return

end subroutine testsub

end module mysubs


program test_ptr_assignment

use mysubs

implicit none

real, pointer, dimension(:) :: test
real, allocatable, dimension(:) :: alloc1, alloc2
real, allocatable, dimension(:) :: alloc1B, alloc2B

allocate ( test (1:5), alloc1 (1:5), alloc1B (1:5) )
test = [ 1.0, 2.0, 3.0, 4.0, 5.0 ]
alloc1 = test
alloc1B = test

write (*, *) "A:", lbound (test, 1), ubound (test, 1)
write (*, *) test

call testsub (test, alloc1, 1, alloc1B )

test (0:4) => test
allocate ( alloc2 (0:4), alloc2B (0:4) )
alloc2 = test
alloc2B = test

write (*, *)
write (*, *) "B:", lbound (test, 1), ubound (test, 1)
write (*, *) test

call testsub (test, alloc2, 0, alloc2B)

stop

end program test_ptr_assignment
1 голос
/ 20 февраля 2012

Вы можете использовать встроенные функции lbound и ubound, чтобы определить границы массива в подпрограмме, например,

program test
  real, dimension(:,:), pointer :: A

  allocate(A(3:5,7:8))
  A = 1
  call my_print(A)

contains

  subroutine my_print(X)
    integer :: i,ml,mu
    real, dimension(:,:), pointer :: X
    ml = lbound(X,1)
    mu = ubound(X,1)
    do i = ml,mu
       write(*,*) X(i,:)
    end do
  end subroutine my_print

end program test
0 голосов
/ 21 февраля 2012

Испорченная форма, на которой настаивает компилятор, означает, что вы не можете объявлять верхние границы фиктивных аргументов.Но вы можете объявить нижние, а верхние вы получите автоматически:

subroutine my_subroutine(arr)
   real, dimension(x0:,y0:) :: arr
   ...
end subroutine my_subroutine

Это верно и для выделяемых массивов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...