在Fortran中,是否有一种可靠的方法可以获得一个转置后的数组指针?

编程语言 2026-07-09

更新:请看我补充的答案,提出一个“略微可接受”的解决方案

对于一个二维数组,在“变量上下文”中表达式 transpose(x) 是不可接受的,因此你不能把它传给某个例程以以某种方式修改数组。如果你想避免把转置拷贝到临时数组(然后再拷回来)所带来的开销,显然有一个指针仅对数组描述符进行转置(即交换步幅和边界)会很有帮助。但遗憾的是,目前没有直接的语法来实现带有swapped indices的指针关联语句……

有趣的是,编译器(我测试的是gfortran 13.3.0)似乎接受一个实现它的函数调用:

module p_hack
  implicit none
contains
  function passoc(a)   ! pointer function, result accepted in variable context 
    integer, target   :: a(:,:)
    integer, pointer  :: passoc(:,:)
    passoc => a        ! just pointing, not yet transposing things
  end function
end

program test
  use p_hack
  integer          :: A(2,2)     ! NB: not even the target attribute is used!
  integer, pointer :: pA(:,:)

  pA => passoc( transpose(a) )     !!! This does it !!!

  pA(1,:) = [10, 20]
  pA(2,:) = [30, 40]

  print *, A(1,:)     ! indeed printing transpose of the above !
  print *, A(2,:)
end

这段输出很好地显示了转置:

        10          30
        20          40

因此显然指针数组描述符确实以转置的顺序访问了数组,且编译器并未创建临时数组。但我并不认为这在所有情形下都能得到保证。也许对原始数组应用 target 属性会有帮助,但更可能的情况是这根本就不符合标准。问题是:

是否可以用标准兼容的方式实现类似的操作,以获得转置后的指针?

同样值得了解的是,Fortran标准化是否已经在考虑扩展对数组指针的使用可能性。因为与之有些相似的特性,如移位索引,实际上是被标准允许的:

pA(1:2, 4:5) => A    ! standard Fortran (if A has target attribute)
pA(3:4, 2:3) => A    !   ... and compiler accepts it

NB:对“向下索引指针”也可以提出类似的问题,但在该情形下,标准已经给出了直接的实现方式:

pA(1:2, 2:1:-1) => A          ! compile error: "Stride must not be present"
pA => passoc( A(:,2:1:-1) )   !!! This does it, but not needed !!!
pA => A(:,2:1:-1)             !  ... because this is already allowed

然而对于转置,似乎没有简单的解决方案。

相关: Returning a Pointer to a Multidimensional Array 是关于C 的,不是Fortran……

解决方案

似乎在某些情况下,获得对“有效参数”的保证指针(防止编译器在输入输出时复制)确实会被标准所支持。在 https://fortran-lang.discourse.group/t/pass-by-reference/5003/7 中我找到了下面这段文字:

If the dummy argument has the TARGET attribute, does not have the VALUE
attribute, and either the effective argument is simply contiguous or the dummy
argument is scalar, assumed-rank, or assumed-shape, and does not have the
CONTIGUOUS attribute, and the effective argument has the TARGET attribute
but is not a coindexed object or an array section with a vector subscript then

* any pointers associated with the effective argument become associated
  with the corresponding dummy argument on invocation of the procedure, and
* when execution of the procedure completes, any pointers that do not
  become undefined (19.5.2.5) and are associated with the dummy argument
  remain associated with the effective argument.

我没有直接链接到标准,但这似乎允许下面给出的一个 ptranspose 函数的(有点初步的)实现,它在变量上下文中是有效的:

module p_hack
  implicit none;  private;   public :: ptranspose
contains
  function change_stride(a, dummy1, d1,d1d2)   result(p)
    integer, pointer                :: p(:,:)
    integer, intent(inout), target  :: a(1:dummy1, 1:d1d2)
    integer                         :: dummy1, d1, d1d2

    p => a(1:d1d2:d1, 1:d1)         ! super tricky reshape
  end

  function ptranspose(a)
    integer, intent(inout), target  :: a(:,:)
    integer, pointer                :: ptranspose(:,:)
    ptranspose => change_stride(a, 1, size(a,1), size(a))
  end function
end module

program test
  use p_hack
  integer, target  :: A(3,2)    ! has to be contiguous array (generalize?)
  integer, pointer :: pA(:,:)

  pA => ptranspose( a ) 

  pA(1,:) = [10, 20, 30]   !!!  Use it as if it is transpose(A) !!!
  pA(2,:) = [40, 50, 60]

  print *, A(1,:)     ! indeed A contains transpose of the above !
  print *, A(2,:)
  print *, A(3,:)
end

这段会输出所需的结果,表明指针确实指向了A,并且跨越的步幅与边界被重新排列了(当然还可以再做一些测试……)

    10   40
    20   50
    30   60

并且如果需要,可以通过现有的Fortran语法简单地添加边界偏移:

   ...
  pA(0:, 0:) => ptranspose( a ) 

  pA(0,:) = [10, 20, 30]   !!!  Use it as if it is transpose(A) !!!
  pA(1,:) = [40, 50, 60]

  print *, A(1,:)     ! indeed A contains transpose of the above !
  print *, A(2,:)
  print *, A(3,:)
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章