I’m trying to find the most compact/clear way to reallocate an array to a different size, keeping both boilerplate and allocations to a minimum and am looking at SOURCEd allocation.
In the example, with gfortran
:
- if the new array is longer, all data is copied from a, and the trailing elements are not initialized (nice)
- if the new array is shorter, sourcing it to a longer array leads to a segfault.
program compact_resize
implicit none
integer :: i,j
integer, allocatable :: a(:),longer(:),shorter(:)
a = [(i,i=1,10)]
print *, 'a=',a
! works
allocate(longer(20), source = a)
print *, 'b=',longer
! segfault
allocate(shorter(5), source = a)
print *, 'c=',shorter
end program compact_resize
Is what I’m doing violating any standards? the Intel documentation says " If object is an array, s-spec must appear and the number of s-specs must equal the rank of object, or source-expr must appear and have the same rank as object and the shape of object is that of source-expr". In other words, it seems like the option of BOTH specifying the size of the array and an array source-expr is not considered.
On Metcalf/Reid/Cohen it states that “if allocation is for an array, source-expr may be an array of the same rank, otherwise source-expr must be scalar”. also “because the bounds and shape of the allocated item are not taken from the source, making a clone of an array has to be done [by specifying its bounds]”, like
allocate(a(lbound(b,1):ubound(b,1)), source = b)
So apparently there’s a constraint on the rank, but not on their size, gfortran is doing it right, but of course it’s overflowing the copy when the new array is shorter than the former…
If you know they are shorter, you could apply bounds to the source:
allocate(shorter(5), source = a[:5])
Or more general:
allocate(shorter(new_size), source = a[:min(new_size, size(a))])
1 Like
This program demonstrates an array constructor with an implied do loop and also reshape
with pad
to grow an array:
program main
implicit none
real, allocatable :: x(:), y(:), z(:)
integer :: i
x = [10.0, 20.0]
y = [x, (0.0,i=1,3)]
z = reshape(x, shape=[5], pad=[0.0])
print "(*(f4.1,1x))", y,z
end program main
! output: 10.0 20.0 0.0 0.0 0.0 10.0 20.0 0.0 0.0 0.0
I have tweeted about using move_alloc
to do so more efficiently.
1 Like
Nice alternatives, thank you both. So I’m guessing the best option is still the sourced allocation, but unfortunately, more bound checking lines will always be necessary like
pure subroutine resize(array,newsize)
type(mytype), intent(inout), allocatable :: array(:)
integer, intent(in) :: newsize
integer :: copysize
type(mytype), allocatable :: tmp(:)
copysize = min(newsize,merge(ubound(array,1),0,allocated(array))
allocate(tmp(newsize),source=array(:copysize))
call move_alloc(from=tmp,to=array)
end subroutine resize