7

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.

5
  • What message do you get if you compile with -C (check bounds flag) and rerun? Commented Sep 23, 2011 at 2:44
  • I get no message and the segmentation violation is persists. Commented Sep 23, 2011 at 2:52
  • 8
    Ugh, functions with side effects and pointers; you're bound to get into trouble. Don't try to write C++ in Fortran. (Although we all know a real programmer can write Fortran in any language.) Commented Sep 23, 2011 at 11:35
  • apart from what the answers suggested, you could copy array(j) to a different variable before entering the function. You should alway be wary of these things in fortran, e.g. calling your function like append(10, array, val) will also segfault, so you could better use integer, intent(inout) :: n. Commented Sep 25, 2011 at 8:50
  • @steabert: Thanks for the hint, the way I posted it this is true. In the real version of the code n, and array are used over a module and the function is called like array(i) = append(array(j)) Commented Sep 26, 2011 at 20:10

3 Answers 3

12

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
Sign up to request clarification or add additional context in comments.

Comments

9

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

Comments

2

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

1 Comment

Welcome to Stack Overflow! Please don't add "thanks" as answers. Invest some time in the site and you will gain sufficient privileges to upvote answers you like, which is the Stack Overflow way of saying thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.