Matthias 009
Matthias 009

Reputation: 1702

Fortran increase dynamic array size in function

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

Answers (3)

victortxa
victortxa

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

janneb
janneb

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

milancurcic
milancurcic

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

Related Questions