指针作为派生类型的组件

时间:2018-02-08 00:33:14

标签: fortran gfortran fortran90

我从这个答案(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 个答案:

答案 0 :(得分:1)

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

  • 为已声明的数组(例如wages_tgt)分配新值。这相当于说orig1%wages_tgt = ret%wages_tgt。这些阵列中的每一个都占据内存中单独的位置。
  • 指针(例如wages)指向源指针当前指向的位置。这相当于说orig1%wages => ret%wages。这两个指针的目标是内存中的相同位置。在此示例中,任何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

答案 1 :(得分:0)

这是我在这里提出的问题的一个额外答案,但并没有明确说明。关于fortran如何在IMO工作的令人困惑的方面是你最终得到了导致不直观行为的圆形指针(即使根据f90规范它是正确的)。

但是明确地从SELECT (SELECT DATEDIFF('2018-02-20', '2018-02-14')) - (SELECT COUNT(id) FROM tbl_holiday WHERE dates BETWEEN '2018-02-14' AND '2018-02-20'); 指向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复制到orig1,而不会产生更改ret的副作用。

然而,遗憾的是,如果我使用关联进行测试,它声称orig2并未指向orig1,即使我明确指出这种方式并且行为似乎也反映出来的是:

orig2