Reputation: 1702
I need a variable size array in Fortran. In C++ I would use vector. So I have a function like
integer function append(n, array, value)
integer, pointer, dimension(:) :: array
integer, pointer, dimension(:) :: tmp_arr
integer n
if (size(array) .eq. n) then
allocate(tmp_arr(2*size(array)))
tmp_arr(1:size(array)) = array
deallocate(array)
array => tmp_arr
end if
n = n + 1
array(n) = value
append = n
end function
that works fine if I use it the way
integer pos, val
pos = append(n, array, val)
However, if I would like to use it the way
integer i,j,n ! i,j<n
array(i) = append(n, array, array(j))
with gfortran this does not work. It compiles, but segfaults. The problem seems to be that gfortran makes addresses out of array(i) and array(j), sends the latter to the function append, and then when the address of array(j) is accessed and the one of array(i) written, the address space has been deallocated.
What I would like is that the value of array(j) is put on the stack (not the address) and then used in the function and after the function has finished the uptodate address of array(i) is looked up and the result of the function saved to it.
I am pretty sure gcc would do it the way I want, why is gfortran so mean?
Is there any way in Fortran to make a robust (meaning the array(j) = ... example works) function or data type to have a c++ stl vector like behaviour?
Conclusion:
I eventually introduced temporary variables
integer tmp_val
tmp_val = value
...
array(n) = tmp_val
so at least the method can be called as
pos = append(n, array, array(j))
array(i) = pos
and hope that other/future developers on the project won't try to 'optimize' the two lines to eliminate the necessity of 'pos'.
Thanks for the answers and comments.
Upvotes: 7
Views: 12683
Reputation: 99
Thank you a lot janneb.It was very helpful your comment.
Only a few change I have made was to omit the deallocate(array)
. There was'nt any erro omitting this line in my code.
This change is specially helpful if you need to put it into a loop and you don't allocated array
before the loop. My specific case follows below (look that I don't allocate x_all
before or into the loop):
begin program test
integer,allocatable::x_all(:),tmp_arr(:)
integer,allocatable::x_tmp(:)
integer::N
allocate(x_tmp(2*N))
(...)
i=1
do while(logical test)
...
x_tmp(i)=some calculus
i=i+1
...
end do
i=i-1
allocate( tmp_arr( 1:(i+size(x_all) ) ) )
tmp_arr(1:size(x_all))=x_all
tmp_arr(size(x_all)+1:)=xtemp
call MOVE_ALLOC(tmp_arr,x_all)
...
end program
Upvotes: 2
Reputation: 37188
The answer by IRO-bot is the correct approach for Fortran 90. If you can limit yourself to compilers that support the Fortran 2003 MOVE_ALLOC intrinsic (included in gfortran since the 4.2 release), you can avoid one of the copies. That is, increasing the size of an array by a factor of 2 can be written as
allocate(tmp_arr(2*size(array)))
tmp_arr(1:size(array)) = array
deallocate(array)
move_alloc(tmp_arr, array)
! tmp_arr is now deallocated
Upvotes: 12
Reputation: 6241
OK, the problem is that you cannot deallocate and re-allocate the array that you are assigning a function value to. You are correct about the cause of your problem (arguments passed by reference and not by value as it is in C). Since you deallocate the array inside the function body, the assignment to that array becomes invalid, leading to segfault. This is not a gfortran issue, tried it with ifort and pgf90, all of them report the same problem. This works for me:
PROGRAM dynamic_size
INTEGER,DIMENSION(:),ALLOCATABLE :: array
ALLOCATE(array(10))
array=(/1,2,5,7,4,3,6,5,6,7/)
WRITE(*,*)SIZE(array)
CALL resize_array
WRITE(*,*)size(array)
CONTAINS
SUBROUTINE resize_array
INTEGER,DIMENSION(:),ALLOCATABLE :: tmp_arr
ALLOCATE(tmp_arr(2*SIZE(array)))
tmp_arr(1:SIZE(array))=array
DEALLOCATE(array)
ALLOCATE(array(size(tmp_arr)))
array=tmp_arr
ENDSUBROUTINE resize_array
ENDPROGRAM dynamic_size
Upvotes: 9