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

发布于 2024-12-06 02:48:03 字数 1292 浏览 0 评论 0原文

我需要 Fortran 中的可变大小数组。在 C++ 中我会使用向量。所以我有一个这样的函数,

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 从 array(i) 和 array(j) 中获取地址,将后者发送到函数append,然后当访问 array(j) 的地址并写入 array(i) 的地址时,地址空间已被释放。

我想要的是,将 array(j) 的值放在堆栈上(而不是地址),然后在函数中使用,在函数完成后,查找 array(i) 的最新地址并得出结果该函数已保存到其中。

我很确定 gcc 会按照我想要的方式做,为什么 gfortran 如此卑鄙?

Fortran 有什么方法可以使一个健壮(意味着 array(j) = ... 示例有效) 函数或数据类型具有类似 C++ stl 向量的行为?

结论:

我最终引入了临时变量

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

,因此至少可以调用该方法

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

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

感谢您的回答和评论。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

少年亿悲伤 2024-12-13 02:48:03

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

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
星光不落少年眉 2024-12-13 02:48:03

好的,问题是您无法取消分配并重新分配要为其分配函数值的数组。您对问题原因的理解是正确的(参数通过引用传递,而不是像 C 中那样通过值传递)。由于您在函数体内释放了数组,因此对该数组的赋值变得无效,从而导致段错误。这不是 gfortran 问题,用 ifort 和 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

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
晒暮凉 2024-12-13 02:48:03

非常感谢您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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文