代码之家  ›  专栏  ›  技术社区  ›  JohnE

指针作为派生类型的组件

  •  3
  • JohnE  · 技术社区  · 7 年前

    我从这个答案中理解( Fortran copy of pointer )我应该尝试使用可分配数组而不是数组指针作为派生类型的组件。我打算这样做,但明年我将被锁定在当前代码中,需要能够使用它并理解它,直到明年我能够做出更大更好的更改。

    我还认为这个问题引起了人们的普遍关注,因为我认为这里的fortran行为非常不直观,尽管表面上是正确的。因此,更好地理解它的工作方式和原因可能很有价值(或者我假设编译器根本不允许我们这样做,对吧?)。

    抱歉,这篇冗长的介绍。我将尝试将其作为一个经过尽可能精简的带注释程序:

    program main
    
       type tRet
          real :: agi
          real :: wages_tgt(5)          ! compiler won't let me declare as
                                        ! target but later I point to this
          real, pointer :: wages(:)
       end type tRet
    
       type(tRet), target :: ret             ! I don't quite understand 
       type(tRet), target :: orig1, orig2    ! why but compiler insists
                                             ! these be targets
       ret%wages => ret%wages_tgt(1:1)
       ret%wages = (/ 11. /)
       orig1 = ret
       ret%wages = (/ 99. /)
       orig2 = ret
    

    这是程序的上半部分,下面是一些打印输出的结果,如右侧的注释所示:

       ! This is not want I want and I was surprised, but it is kind
       ! of explained in the other answer why this happens, so OK...
    
       print *, "orig1%wages ", orig1%wages    ! 99.
       print *, "orig2%wages ", orig2%wages    ! 99.
    
    
       ! But if I copy from orig1 or orig2 into ret then it
       ! works like I wanted it to work in the first place!
       ! But I don't completely understand why it works...
    
       ret = orig1
       print *, "ret%wages   ", ret%wages      ! 11.
       print *, "orig1%wages ", orig1%wages    ! 11.
       print *, "orig2%wages ", orig2%wages    ! 11.
    
       ret = orig2
       print *, "ret = orig2 "
       print *, "ret%wages   ", ret%wages      ! 99.
       print *, "orig1%wages ", orig1%wages    ! 99.
       print *, "orig2%wages ", orig2%wages    ! 99.
    
    end program main
    

    我很高兴能对这里发生的一切做出很好的解释。我想,具有讽刺意味的是,我并不太担心为什么这是一个坏主意,而是担心为什么我的变通方法似乎工作得很好?

    或者,总结我的问题最简单的方法是: 到底指的是什么?

    编译器:GNU Fortran(GCC)4.8.5 20150623(Red Hat 4.8.5-16)

    2 回复  |  直到 6 年前
        1
  •  1
  •   JohnE    7 年前

    这里发生的是,当您复制派生数据类型时,派生类型的每个组件都会发生不同的事情。当你这样做的时候 orig1 = ret :

    • 声明的数组,例如 wages_tgt ,将被指定新值。这相当于说 orig1%wages_tgt = ret%wages_tgt . 每个阵列占用 分离 内存中的位置。
    • 指针,例如 wages ,使其指向源指针当前指向的任何位置。这相当于说 orig1%wages => ret%wages . 这两个指针的目标都是 相同的 内存中的位置。在这里的示例中,任何 工资 ret%wages_tgt .

    考虑到这一点,你的结果对我来说很有意义。添加一些选择性附加注释:

    ret%wages => ret%wages_tgt(1:1)
    
    ret%wages = (/ 11. /)   ! so wages_tgt = 11 also
    
    orig1 = ret             ! This is BOTH a copy and re-point
                            ! * orig1%wages_tgt =  ret%wages_tgt (11)
                            ! * orig1%wages     => ret%wages_tgt
    
    ret%wages = (/ 99. /)   ! this changes ret%wages & ret%wages_tgt
                            ! to 99 and also orig1%wages since it is
                            ! also pointing to ret%wages_tgt.
    
                            ! note that orig1%wages_tgt is UNCHANGED 
                            ! (still 11) but nothing is pointing to it!
    

    代码下方。。。

    ret = orig1  ! ret%wages_tgt = orig1%wages_tgt (11) 
                 ! no repointing actually happens this time b/c we have
                 ! set up a circular relationship between all the
                 ! pointers such that ALL of them point to ret%wages_tgt
    
        2
  •  0
  •   JohnE    7 年前

    这是对我在这里提出的一个问题的一种额外的回答,但我并没有真正明确回答。在我看来,fortran在这里的工作方式令人困惑的一点是,最终使用的循环指针会导致不直观的行为(即使它根据f90规范是正确的)。

    但通过明确指出 orig1%wages orig1%wages_tgt (对于 orig2 )至少在某种程度上,您可以避免使用循环指针。这里的代码和问题中的代码相同,但添加了一些明确的指向。

    ret%wages => ret%wages_tgt(1:1)
    ret%wages = (/ 11. /)
    
    orig1 = ret
    orig1%wages => orig1%wages_tgt(:size(ret%wages))   ! *** added code ***
    
    ret%wages = (/ 99. /)
    
    orig2 = ret
    orig2%wages => orig2%wages_tgt(:size(ret%wages))   ! *** added code ***
    
    print *, "orig1%wages ", orig1%wages    ! 11.
    print *, "orig2%wages ", orig2%wages    ! 99.
    
    ret = orig1
    print *, "ret%wages   ", ret%wages      ! 11.
    print *, "orig1%wages ", orig1%wages    ! 11.
    print *, "orig2%wages ", orig2%wages    ! 99.
    
    ret = orig2
    print *, "ret = orig2 "
    print *, "ret%wages   ", ret%wages      ! 99.
    print *, "orig1%wages ", orig1%wages    ! 11.
    print *, "orig2%wages ", orig2%wages    ! 99.
    

    因此 orig1 & orig2 指针不同(并避免循环指向),您可以复制 原始版本1 ret 没有改变的副作用 orig2 .

    然而,这里剩下的一件奇怪的事情是,如果我用关联的 原始版本1 不指向 orig2 尽管我明确指出了这一点,但这种行为似乎也反映了:

    print *, "assoc?", associated(orig1%wages, orig1%wages_tgt) ! F