Fortran increases dynamic array size in function

I need an array of variable sizes in Fortran. In C ++, I would use a vector. Therefore, 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 

which works great if i use it in a way

 integer pos, val pos = append(n, array, val) 

However, if I would like to use it in a way

 integer i,j,n ! i,j<n array(i) = append(n, array, array(j)) 

with gfortran it does not work. It compiles, but segfaults. The problem is that gfortran makes addresses from array (i) and array (j), sends the latter to the append function, and then when the address of array (j) is accessed and one of array (i) is written, the address space has been freed .

I would like the value of array (j) to be pushed onto the stack (rather than 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 stored function.

I'm sure gcc will do it the way I want, why is gfortran so boring?

Is there any way in Fortran to make it reliable (which means an example of an array (j) = ...) of a function or data type to have a C ++ stl vector as a behavior?

Conclusion:

I ended up introducing temporary variables

 integer tmp_val tmp_val = value ... array(n) = tmp_val 

therefore, at least the method can be called

 pos = append(n, array, array(j)) array(i) = pos 

and hope that other / future project developers will not try to β€œoptimize” the two lines to eliminate the need for β€œpos.”

Thanks for the answers and comments.

+6
source share
3 answers

The answer of IRO-bot is the right approach for Fortran 90. If you can limit yourself to compilers that support Fortran 2003 MOVE_ALLOC (included in gfortran since version 4.2 was released), you can avoid one of the copies. That is, an increase in the size of the array by 2 times 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 
+12
source

OK, the problem is that you cannot free and reassign the array to which the function value is assigned. You are right about the cause of your problem (arguments are passed by reference, not by value, as in C). Since you are freeing the array inside the function body, the purpose of this array becomes invalid, which leads to segfault. This is not a gfortran problem, having tried it with ifort and pgf90, they all 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 
+9
source

Thanks a lot janneb . This was a very helpful comment.

Only a few changes I made are to omit deallocate(array) . There was no erro missing this line in my code. This change is especially useful if you need to put it in a loop and you did not allocate an array before the loop. Below is my specific case (see that I do not x_all before or in 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 
+2
source

Source: https://habr.com/ru/post/897873/


All Articles