From: Ragu on
Pointer nullify and deallocate

I have to use an array of pointers for my program. I am thinking that
an array of pointers will be more efficient to code and easier to
maintain than handling three multi-dimensional arrays for x, y and z
directions. I started my first test program after browsing the text
books (MRC and Chapman). In S.J.Chapman. 3rd edition, Chapter 15, Page
756 it is said thus:
“When dynamic memory is deallocated in a DEALLOCATE statement, the
pointer to the memory is automatically nullified. However, if there
are other pointers pointing to that same memory, they must be manually
nullified or reassigned. If not, …”

In MRC F95/2003, Page 106, Section 6.5.4 it is said that
“Because often there are other ways to access a target (for example,
through another pointer), the NULLIFY statement does not deallocate
the targets. If deallocation is also required, a DEALLOCATE statement
should be executed instead.”

I haven’t checked F2003 handbook yet.

While I was playing with my test program, I found out that MRC is
correct and that SJC is not so (or I am misinterpreting SJC or not
understanding SJC correctly). In the test program, I get a YES for
associated status after a deallocate.

So my question is:
1) Am I interpreting SJC correctly?
2) Should a deallocate be executed first before nullify or it does not
matter? I am thinking that it should not matter as long as they are
done one after another.
3) As I am planning to use this sample technique for multi-dimensional
arrays, are there any best-practices / pitfalls that I should be aware
of?

Thanks.

program firstpointer
implicit none
integer, parameter :: dp_k = selected_real_kind(p=15, r=300)
real(kind=dp_k), dimension(:), allocatable, target :: x, y, z
integer :: ii
type dim1arrptr
real(kind=dp_k), dimension(:), pointer :: p => null()
end type dim1arrptr

type (dim1arrptr), dimension(3) :: val

continue

! Fill array with data
allocate(x(3), y(2), z(4))
x = (/(real(ii,kind = dp_k), ii = 2,4) /)
y = (/(real(ii,kind = dp_k), ii = 3,4) /)
z = (/(real(ii,kind = dp_k), ii = 4,7) /)
! print values
write(*, '(A)') 'Initial Values'
write(*, '(A,4F5.2)') 'array x : ', x
write(*, '(A,4F5.2)') 'array y : ', y
write(*, '(A,4F5.2)') 'array z : ', z

! associate arrays with pointer
val(1)%p => x
val(2)%p => y
val(3)%p => z

! print values
write(*, '(/A)') 'Pointer Values'
write(*, '(A,4F5.2)') 'val(1) : ', (val(1)%p(ii), ii = 1, 3)
write(*, '(A,4F5.2)') 'val(2) : ', (val(2)%p(ii), ii = 1, 2)
write(*, '(A,4F5.2)') 'val(3) : ', (val(3)%p(ii), ii = 1, 4)
write(*,'(A,L1)') 'val(1)%p associated ? ', associated(val(1)%p)
write(*,'(A,L1)') 'val(2)%p associated ? ', associated(val(2)%p)
write(*,'(A,L1)') 'val(3)%p associated ? ', associated(val(3)%p)

! deallocate
write(*, '(/A)') 'Deallocate X, Y, Z arrays'
deallocate(x, y, z, STAT = ii)
if(ii /= 0) then
write(*,'(A)') 'Dealloc was not successful'
else
write(*,'(A)') 'Dealloc was successful'
endif
write(*,'(A,L1)') 'val(1)%p associated ? ', associated(val(1)%p)
write(*,'(A,L1)') 'val(2)%p associated ? ', associated(val(2)%p)
write(*,'(A,L1)') 'val(3)%p associated ? ', associated(val(3)%p)

! nullify
write(*, '(/A)') 'Nullify Pointers'
if(associated(val(1)%p)) nullify(val(1)%p)
if(associated(val(2)%p)) nullify(val(2)%p)
if(associated(val(3)%p)) nullify(val(3)%p)
write(*,'(A,L1)') 'val(1)%p associated ? ', associated(val(1)%p)
write(*,'(A,L1)') 'val(2)%p associated ? ', associated(val(2)%p)
write(*,'(A,L1)') 'val(3)%p associated ? ', associated(val(3)%p)

end program firstpointer
From: Ragu on
Here's the output:

Initial Values
array x : 2.00 3.00 4.00
array y : 3.00 4.00
array z : 4.00 5.00 6.00 7.00

Pointer Values
val(1) : 2.00 3.00 4.00
val(2) : 3.00 4.00
val(3) : 4.00 5.00 6.00 7.00
val(1)%p associated ? T
val(2)%p associated ? T
val(3)%p associated ? T

Deallocate X, Y, Z arrays
Dealloc was successful
val(1)%p associated ? T
val(2)%p associated ? T
val(3)%p associated ? T

Nullify Pointers
val(1)%p associated ? F
val(2)%p associated ? F
val(3)%p associated ? F
From: Richard Maine on
Ragu <ssragunath(a)gmail.com> wrote:

> Pointer nullify and deallocate
>
> I have to use an array of pointers for my program. I am thinking that
> an array of pointers will be more efficient to code and easier to
> maintain than handling three multi-dimensional arrays for x, y and z
> directions. I started my first test program after browsing the text
> books (MRC and Chapman). In S.J.Chapman. 3rd edition, Chapter 15, Page
> 756 it is said thus:
> "When dynamic memory is deallocated in a DEALLOCATE statement, the
> pointer to the memory is automatically nullified. However, if there
> are other pointers pointing to that same memory, they must be manually
> nullified or reassigned. If not, …"
>
> In MRC F95/2003, Page 106, Section 6.5.4 it is said that
> "Because often there are other ways to access a target (for example,
> through another pointer), the NULLIFY statement does not deallocate
> the targets. If deallocation is also required, a DEALLOCATE statement
> should be executed instead."
>
> I haven't checked F2003 handbook yet.
>
> While I was playing with my test program, I found out that MRC is
> correct and that SJC is not so (or I am misinterpreting SJC or not
> understanding SJC correctly). In the test program, I get a YES for
> associated status after a deallocate.
>
> So my question is:
> 1) Am I interpreting SJC correctly?

No, you are not. Both books are correct and say the same thing. Your
quote from MRC starts out talking about the deallocate statement, while
the one from SJC starts out talking about the nullify statement, but
both get around to saying the same things (and both do so correctly).

A deallocate statement deallocates and nullifies.

A nullify statement just nullifies, but does not deallocate.


> 2) Should a deallocate be executed first before nullify or it does not
> matter? I am thinking that it should not matter as long as they are
> done one after another.

Phrased the way you just did, it matters a lot. If you want to
deallocate the space, you must use a deallocate statement. There is no
other way (for pointers). Nullify will not do it. Nor will anything
else. Doing a nullify after the deallocate is pointless, but also mostly
harmless. Probably the main element of harm is in the confusion.

Oh. hold on.... I now that I look at your code, I see what is going on.
It wasn't evident to me from the words. You have both allocatables and
pointers here. That makes a difference. All the above comments are true
(including those in both books), but they are solely about pointers.
Allocatables are different (and can never be nullified - the concept
does not apply).

Your code requires both the deallocate and the nullify (at least to be
clean). That's because you have 2 separate things - the allocatable and
a pointer to it. You need the deallocate to deallocate the allocatable.
But that will only deallocate the allocatable. It won't do anything to
the pointer that points to it. That pointer will be left trying to point
to space that is no longer allocated. It is good practice to nullify the
pointer to get it out of that state.

Note that even asking about the association status of the pointer is
illegal right after the deallocate. Any results you might get from such
are likely to be inconsistent garbage (and might even crash the
program). That's part of why you want to nullify it.

But.... I wouldn't do it this way in the first place....

> 3) As I am planning to use this sample technique for multi-dimensional
> arrays, are there any best-practices / pitfalls that I should be aware
> of?

Looks to me like you are just looking for allocatable components with no
need for pointers to be involved. If that's the case, I recommend you do
it that way. Pointers have *MANY* pitfalls. They can be used as
substitutes for allocatables, but they are much more confusing in that
role and you are far more likely to get things wrong. Allocatables
should just work. You have added the extra complication of having both
an allocatable and a separate pointer to it. As far as I can see, you
don't need the two things. If you just have the allocatable components,
with no pointers, things should be much simpler.

Allocatable components do require either f2003 or the allocatable TR to
f95, but most compilers support at least the TR by now.

--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain
From: Ragu on
Richard,

Thanks. I believe that I need the pointers to allocatable arrays. Let
me explain my situation:

I have an array currently declared as
real(kind = dp_k), dimension(:,:,:), allocatable :: tfs
! tfs or Transfer Functions
! Rank 1 --> nfreq or number of frequencies
! Rank 2 --> 3*3 Directions
! Rank 3 --> nnodes or number of nodes

In the current setup, I loop across the nine directions while printing
to output files. The code is clean. The values are printed to output
as
Do ii = 1, 9
write(funit,'(100F16.4)') maxval(tfs(1:nfreq,ii,jj),dim = 1),jj = 1,
nnodes)
End do ! ii, 1 to 9

The above assumes that the nnodes and nfreq are same for the 9
directions. The newer situation demands that a situation where nfreq
are same for the first 3, next 3 and last 3 of Rank=2.

So I am trying to split it as
real(kind = dp_k), dimension(:,:,:), allocatable :: xtfs, ytfs, ztfs
! Rank 1 --> nfreq(idir) or Total number of frequencies
! Rank 2 --> 3 Directions (not 3*3)
! Rank 3 --> nnodes

When I am adding an extra dimension to the problem, then I allocate
memory as
Select case(idir)
Case(1)
allocate(xtf(nfreq(idir),3,nnodes))
Case(2)
allocate(ytf(nfreq(idir),3,nnodes))
Case(3)
allocate(ztf(nfreq(idir),3,nnodes))
End select

Then I have to link them to array of pointers.
type dim3arrptr
real(kind=dp_k), dimension(:,:,:), pointer :: p => null()
end type dim3arrptr

type (dim3arrptr), dimension(3) :: tfs

! assign pointers as
tfs(1)%p => x
tfs(1)%p => y
tfs(1)%p => z

When I am printing the results, I can do
Do ii = 1, 3
Do jj = 1, 3
write(funit,'(100F16.4)') maxval(tfs(ii)%p(1:nfreq(ii),jj,kk),dim
= 1),kk = 1, nnodes)
End do ! jj, 1 to 3 minor dirs
End do ! ii, 1 to 3 arrays

This will maintain the old code structure and avoid splitting the
existing code into a repetitive mode for printing arrays xtfs, ytfs
and ztfs.

I haven't written the actual code and compiled. So this is kind of a
pseudo code. Do you think this is the right path?

Thanks.
Ragu
From: Richard Maine on
Ragu <ssragunath(a)gmail.com> wrote:

> Thanks. I believe that I need the pointers to allocatable arrays. Let
> me explain my situation:

I don't see how this translates into needing pointers to allocatable
arrays. You showed how you are accessing the data through the pointers,
but that doesn't illustrate the need for them to be pointers as far as I
can see. You only need pointers when something is going to be accessed
via 2 or more different ways. You only showed one way, so I can do that
without pointers.

> When I am adding an extra dimension to the problem, then I allocate
> memory as
> Select case(idir)
> Case(1)
> allocate(xtf(nfreq(idir),3,nnodes))
> Case(2)
> allocate(ytf(nfreq(idir),3,nnodes))
> Case(3)
> allocate(ztf(nfreq(idir),3,nnodes))
> End select
>
> Then I have to link them to array of pointers.
> type dim3arrptr
> real(kind=dp_k), dimension(:,:,:), pointer :: p => null()
> end type dim3arrptr
>
> type (dim3arrptr), dimension(3) :: tfs
>
> ! assign pointers as
> tfs(1)%p => x
> tfs(1)%p => y
> tfs(1)%p => z

I assume this is supposed to be index values 1, 2, and 3. Anyway...

Instead of making an array of pointers (with the usual derived-type
trick), you could just make an array of allocatables using the same
trick, as in

type dim3allocptr
real(kind=dp_k), allocatable, dimension(:,:,:) :: p
end type
type(dim3allocptr), dimension(3) :: tfs

Then just directly allocate tfs(1)%p(nfreq(idir),3,nnodes),
and likewise for tfs(2)%p and tfs(3)%p.

Now if you want to have that structure and *ALSO* keep some other
structure such as the separate xtf, ytf, and ztf, then you would have an
argument for pointers. Perhaps you do. But this wasn't clear to me.

My main point is that you don't need pointers for any one way of
accessing the data. You only need pointers if you are accessing it via
two different routes. That's pretty much one of the most fundamental
differences between pointers and allocatables. There are some other
cases, but that's a biggie.

--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain