Fortran可分配数组和指针之间的等价性

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Fortran可分配数组和指针之间的等价性相关的知识,希望对你有一定的参考价值。

我有一个带有可分配数组A的Fortran程序,如下所示:

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

我想用C库中实现的自定义内存分配函数allocate替换Fortran的my_alloc语句。我将第一个代码示例更改为:

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

上面的代码不起作用;格福兰说

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)。

答案

是的,由于c_f_pointer的限制,这是一个问题。正如您所发现的那样,内部c_f_pointer仅支持从索引1开始的边界。人们经常声明Fortran是一种单索引语言,但事实并非如此。一个索引只是默认值,Fortran长期支持声明程序员想要的任何起始绑定。因此,c_f_pointer强制您使用一个索引是向后退一步。但是使用Fortran 2003有一个修复:指针边界重新映射:

arr (0:n-1) => arr

而不是1:n,或任何你想要的。

然后将数组传递给子例程,它将接收预期的边界。

编辑:改进演示程序,显示allocatables和指针之间的区别。指针传递数组的边界。一个常规数组传递形状...如果你愿意,可以在子程序中声明第一个维度,然后让形状控制第二个维度。

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
另一答案

您可以使用lboundubound内置函数来查找子例程中数组的边界,例如:

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
另一答案

编译器坚持的延迟形状意味着,您不能声明伪参数的上限。但是您可以声明较低的那些,并且您将自动获得较高的那些:

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

对于可分配数组也是如此。

以上是关于Fortran可分配数组和指针之间的等价性的主要内容,如果未能解决你的问题,请参考以下文章

Fortran 可分配数组生命周期

如何将可分配数组传递给 Fortran 中的子例程

Fortran在gdb中打印可分配数组

分配时Fortran可分配数组的内存位置?

Fortran - 可分配派生类型的可分配数组

C语言 | 每日基础(31)