Fortran在函数中增加动态数组的大小



我需要一个可变大小的Fortran数组。在c++中,我会使用vector。我有一个函数,比如

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
如果我像 那样使用

就可以了

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

但是,如果我想用

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

与gfortran这不起作用。它可以编译,但会出现分段错误。问题似乎是gfortran从数组(i)和数组(j)中获取地址,将后者发送给函数append,然后当访问数组(j)的地址并写入数组(i)的地址时,地址空间已被释放。

我想要的是数组(j)的值被放在堆栈上(而不是地址),然后在函数中使用,在函数完成后,查找数组(I)的更新地址,并将函数的结果保存到它。

我很确定gcc会按照我想要的方式做,为什么gfortran这么刻薄?

是否有任何方法在Fortran中使一个鲁棒(意思是数组(j) =…示例工作)函数或数据类型有一个c++ STL类似的行为?

结论:

我最终引入了临时变量

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

所以至少这个方法可以被调用为

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

并希望项目中的其他/未来的开发人员不会试图"优化"这两行以消除"pos"的必要性。

谢谢你的回答和评论。

IRO-bot的答案是Fortran 90的正确方法。如果您可以限制自己使用支持Fortran 2003 MOVE_ALLOC内在特性的编译器(自4.2版本以来包含在gfortran中),则可以避免其中一个副本。也就是说,将数组的大小增加2倍可以写成


allocate(tmp_arr(2*size(array)))
tmp_arr(1:size(array)) = array
deallocate(array)
move_alloc(tmp_arr, array)
! tmp_arr is now deallocated

好的,问题是您不能释放和重新分配您正在分配函数值的数组。你对问题的原因是正确的(通过引用传递参数,而不是像C中那样通过值传递参数)。由于你在函数体内释放了数组,对该数组的赋值变得无效,导致段错误。这不是一个fortran问题,我试了试pgf90,它们都报告了同样的问题。

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

非常感谢janneb。你的评论很有帮助。

我所做的一些更改是省略了 deallocate(array) 。在我的代码中省略这一行没有任何错误。如果您需要将其放入循环并且在循环之前没有分配array,则此更改特别有用。我的具体情况如下(注意,我没有在循环之前或循环中分配x_all):

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

最新更新