【问题标题】:Fortran increase dynamic array size in functionFortran 在函数中增加动态数组大小
【发布时间】:2011-09-23 01:08:33
【问题描述】:

我需要一个 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)的地址被写入时,地址空间已被释放。

我想要的是数组(j)的值被放入堆栈(而不是地址),然后在函数中使用,在函数完成后查找数组(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”的必要性。

感谢您的回答和cmets。

【问题讨论】:

  • 如果你用 -C 编译(检查边界标志)并重新运行,你会得到什么消息?
  • 我没有收到任何消息,并且分段违规仍然存在。
  • 呃,有副作用和指针的函数;你一定会遇到麻烦。不要尝试在 Fortran 中编写 C++。 (虽然我们都知道真正的程序员可以用任何语言编写 Fortran。)
  • 除了建议的答案之外,您可以在输入函数之前将array(j) 复制到不同的变量中。您应该始终警惕 fortran 中的这些事情,例如像append(10, array, val) 这样调用你的函数也会出现段错误,所以你最好使用integer, intent(inout) :: n
  • @steabert:感谢您的提示,我发布它的方式是正确的。在代码 n 的真实版本中,在模块上使用了数组,函数的调用方式类似于 array(i) = append(array(j))

标签: fortran


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    好的,问题是您无法取消分配和重新分配要为其分配函数值的数组。您对问题的原因是正确的(通过引用而不是 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
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 欢迎来到 Stack Overflow!请不要添加“谢谢”作为答案。在网站上投入一些时间,您将获得足够的privileges 来为您喜欢的答案投票,这是 Stack Overflow 表达谢谢的方式。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-13
      • 2013-05-17
      • 1970-01-01
      • 2017-02-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多