Reputation: 30444
I understand from this answer (Fortran copy of pointer) that I should try to use allocatable arrays rather than array pointers as components of derived types. I intend to do so but am locked into my current code for the next year and need to be able to use it and understand it until I can make the bigger and better changes next year.
I also think this question is of some general interest because I think fortran behavior here is pretty unintuitive although apparently correct. So there is likely value in better understanding how and why it works (or else I assume the compiler wouldn't let us do this at all, right?).
Sorry for the longish intro. I'll try to do this as one annotated program that has been whittled down as much as possible:
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
That's the top half of the program, here's some results with print output shown as comments to the right:
! 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
I'm happy for any nice explanation of what is going on here. The irony here, I guess, is that I'm not so worried about why this is a bad idea but rather why does my workaround seem to work fine?
Or maybe the easiest way to summarize my question is: What exactly is pointing to what?
Compiler: GNU Fortran (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16)
Upvotes: 3
Views: 551
Reputation: 30444
This is sort of a bonus answer to a question I had here but didn't really make explicit. The confusing aspect to how fortran works here IMO is that you end up with circular pointers that leads to unintuitive behavior (even if it correct according to the f90 specification).
But by explicitly pointing from orig1%wages
to orig1%wages_tgt
(and similarly for orig2
) you can avoid the circular pointers, at least to some degree. Here's the same code as in the question, but with the some explicit pointing added.
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.
Hence, by keeping orig1
& orig2
pointers distinct (and avoiding circular pointing), you can copy orig1
to ret
without the side effect of changing orig2
.
A remaining strange thing here, however, is that if I test with associated, it claims orig1
does not point to orig2
even though I explicitly pointed in that way and the behavior seems to also reflect that:
print *, "assoc?", associated(orig1%wages, orig1%wages_tgt) ! F
Upvotes: 1
Reputation: 2179
What is happening here is that when you copy a derived data type, different things happen for each component of the derived type. When you do orig1 = ret
:
wages_tgt
, are assigned new values. This is equivalent to saying orig1%wages_tgt = ret%wages_tgt
. Each of these arrays occupy separate places in memory.wages
, are made to point to wherever the source pointer is currently pointing. This is equivalent to saying orig1%wages => ret%wages
. The destination of both of these pointers is the same location in memory. In the example here, the only location in memory ever pointed to by any wages
is ret%wages_tgt
.With that in mind, your result makes sense to me. Adding some selective additional annotations:
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!
Lower down in the code...
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
Upvotes: 2