2

I am trying to create an array of pointers in Fortran 90, as described here. Each pointer in this array is then associated with an array of floats, to be allocated at run time by dereferencing the pointer. See the below example:

program test
    type ptr
        double precision, pointer :: p(:)
    end type ptr

    integer :: i, n=5
    type(ptr), dimension(3) :: ptrs
    double precision, pointer :: a(:), b(:), c(:)

    ptrs(1)%p => a
    ptrs(2)%p => b
    ptrs(3)%p => c

    do i=1,3
        allocate(ptrs(i)%p(n))
        ptrs(i)%p = 0d0
    enddo

    write(6, *) ptrs(1)%p
    write(6, *) a

end program test

Result:

$ gfortran -o test test.f90 && ./test                                                                                                 
0.0000000000000000        0.0000000000000000        0.0000000000000000        0.0000000000000000        0.0000000000000000
At line 20 of file test.f90 (unit = 6, file = 'stdout')
Internal Error: list_formatted_write(): Bad type

The error is thrown because a is not allocated. In an ideal world, ptrs(1)%p and a should be identical, but apparently this world is flawed. My question: How do I correctly allocate a, b, and c?

MPA
  • 1,878
  • 2
  • 26
  • 51
  • Please use tag [tag:fortran] for Fortran questions, Also, the language has been called Fortran, not FORTRAN, for several decades (since Fortran 90). – Vladimir F Героям слава Apr 22 '20 at 11:17
  • Both answers are decent description of your error. As a further comment, if at all possible, you should avoid pointers. And, yes, I know pointers are needed for things like link lists and queue. – evets Apr 22 '20 at 16:53

2 Answers2

3

I suggest to always nullify your pointers. That way you won't hit the undefined behaviour cased by undefined pointers so easily.

With the derived type you can use default initialization

type ptr
    double precision, pointer :: p(:) => null()
end type ptr

double precision, pointer :: a(:), b(:), c(:)

The pointer a points to some undefined garbage address.

ptrs(1)%p => a

Now ptrs(1)%p points to the same garbage address.

allocate(ptrs(i)%p(n))

Now a new target is allocated and ptrs(1)%p points there. a is unaffected.

Instead, you now have to point a to that new address as well.

a => ptrs(1)%p

It is important not to consider a and ptrs(1)%p to be two aliases to the same thing. They are not. Both are pointers and both point somewhere. It is your responsibility to make sure they point always to the same address.

2

The important thing to note here is that with

allocate(ptrs(i)%p(n))

you are not allocating (according to the question title) a dereferenced pointer. That is, the allocation does not affect the pointer's target allocation status. Instead, this allocation creates a new target for ptrs(i)%p.

In the answer linked from the question, the target arrays are not themselves pointer or allocatable objects, they are explicit shape arrays. This leads to a subtle difference in what approaches are open to you.

If you want to use dynamic association with the targets of the component pointers, then you can point the pointer to the target after the target is itself allocated. Something like:

allocate(a(n))
ptrs(1)%p => a

Alternatively, you could consider just using allocatable components:

type ptr
  double precision, allocatable :: p(:)
end type ptr
francescalus
  • 30,576
  • 16
  • 61
  • 96