指针作为派生类型的组成部分
Pointers as components of derived types
我从这个答案 () 了解到,我应该尝试使用可分配数组而不是数组指针作为派生类型的组件。我打算这样做,但明年我被锁定在我当前的代码中,需要能够使用它并理解它,直到明年我可以做出更大更好的改变。
我还认为这个问题具有一定的普遍意义,因为我认为此处的 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)
这里发生的事情是,当您复制派生数据类型时,派生类型的每个组件都会发生不同的事情。当你做 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
这是对我在这里提出的一个问题的额外回答,但并没有真正明确。 Fortran 在 IMO 中的工作方式令人困惑的方面是,您最终会得到导致不直观行为的循环指针(即使它根据 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
指针不同(并避免循环指向),您可以将 orig1
复制到 ret
而不会产生更改 orig2
.
然而,这里还有一个奇怪的事情是,如果我用 associated 进行测试,它声称 orig1
没有指向 orig2
,即使我明确地指向了 orig2
并且行为似乎还反映:
print *, "assoc?", associated(orig1%wages, orig1%wages_tgt) ! F
我从这个答案 (
我还认为这个问题具有一定的普遍意义,因为我认为此处的 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)
这里发生的事情是,当您复制派生数据类型时,派生类型的每个组件都会发生不同的事情。当你做 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
这是对我在这里提出的一个问题的额外回答,但并没有真正明确。 Fortran 在 IMO 中的工作方式令人困惑的方面是,您最终会得到导致不直观行为的循环指针(即使它根据 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
指针不同(并避免循环指向),您可以将 orig1
复制到 ret
而不会产生更改 orig2
.
然而,这里还有一个奇怪的事情是,如果我用 associated 进行测试,它声称 orig1
没有指向 orig2
,即使我明确地指向了 orig2
并且行为似乎还反映:
print *, "assoc?", associated(orig1%wages, orig1%wages_tgt) ! F