【问题标题】:Extending an allocatable array with move_alloc and sourced allocation使用 move_alloc 和源分配扩展可分配数组
【发布时间】:2016-02-27 15:58:20
【问题描述】:

在下面的代码中,我试图增加a(:)的长度,首先分配一个更大的临时数组buf(:),将a(:)的内容复制到buf(:),然后使用move_alloc()将数组描述符从buf 复制到a

program main
    implicit none
    integer, allocatable :: a(:), buf(:)

    allocate( a(2) )
    a = [1,2]

    allocate( buf( 4 ), source= 0 )     !! (1)
    buf( 1:2 ) = a( 1:2 )               !! (2)

    ! allocate( buf( 4 ), source= a )   !! (3)

    ! deallocate( a )                   !! (4)
    call move_alloc( buf, a )

    print *, "a(:) = ", a(:)
end

在这里,我有两个问题:首先,在第 4 行中,是否有必要在调用 move_alloc() 之前显式释放 a(:)...?即使没有第 4 行,gfortran 和 Intel Fortran 似乎都可以工作,但这是否意味着 move_alloc() 自动解除分配 a(:) 如果它是预先分配的?其次,我们能否将第 1 行和第 2 行替换为 3,即,对不同长度的数组使用源分配?我已经对此进行了试验,而且 gfortran 和 Intel Fortran 似乎都接受了它并获得了(显然)正确的结果。

【问题讨论】:

  • 还有另一种方式a=[a,0,0]到达终点。
  • [除了缺少)i 的声明] 最终表达式没问题。源术语是一个通用表达式(有一些限制 - 松散地,不依赖于分配的东西),它会在初始时被评估一次。
  • 我尝试了allocate( buf(4), source=[a,0,0] )allocate( buf(4), source=[a, (0, i=3,4)] ),都运行良好(没有运行时错误和正确的结果)。 [...] 的这种用法似乎非常灵活并且可能很有用...
  • 糟糕,交换了 cmets。到目前为止我还不知道数组构造函数的这种语法(用于包含整个数组),所以我想我今天学习了很多 :)

标签: arrays fortran allocation


【解决方案1】:

从 Fortran 标准 (2008, 13.7.118) 关于 move_alloc 我们有参数 to 的详细信息(子例程的第二个参数)

这是一个意图(OUT)参数

与您在 gfortran manual 的链接中给出的描述相匹配。

对于任何其他具有intent(out)allocatable 属性的虚拟参数,与to 关联的实际参数在调用子例程move_alloc 时被释放。也就是说,您不需要自己解除分配a

关于源分配,我们already know表示被分配的数组必须与源数组一致,这样才能写

allocate(buf(4), source=a)  ! a is the same rank as buf, but extent 2

无效,和

一样
allocate(buf(4), source=a(1:2))

是。

然而,虽然有一些关于来源分配的编号限制,但这种符合要求的要求并不包含在其中之一中。因此,您的编译器不需要检测您的代码是否不合格。

当然,编译器允许抱怨,正如您在使用a(1:2) 作为源代码时看到的那样。在某种程度上,在编译时更容易看到范围为 4 的数组与a(1:2) 不一致,而不是a。您可以使用 a(1:SIZE(a)) 进行试验,看看您的编译器在进行此类测试时付出了多少努力。

作为最后的评论,当然可以用单一来源的分配替换您的第 1 行和第 2 行:

allocate(buf, source=[a,0,0])  ! Buf is 2 longer than a

但这并不一定是“更好”。

【讨论】:

  • @francescalus 非常感谢您的详细解释。我不知道如果虚拟参数同时具有 ALLOCATABLE 和 INTENT(OUT),实际参数将被自动释放(如有必要)。确实,这是我第一次遇到这种情况,因为我只将 ALLOCATABLE 虚拟对象用于仍然未分配的数组的“新分配”(在我的其他程序中)。
  • 关于源分配,我在阅读您的解释后尝试了其他几种模式。首先,我用gfortran -fcheck=all 编译了allocate( buf(4), source=a ),然后它给出了关于数组边界的运行时错误。所以似乎 allocate() 尝试读取 a(3) 和 a(4) 以将它们复制到 buf(3:4)。另一方面,ifort -check all 没有给出source=a 的运行时错误(显然为 buf(3:4) 填充 0),因此对于这个不符合要求的代码,行为似乎有所不同。
  • 接下来,对于allocate( buf(4), source=a(1:size(a)) )allocate( buf(4), source=a(:) ),如果没有给出任何选项,ifort 没有任何抱怨,但是当指定 -check all 时它给出了运行时错误。 gfortran 的行为也类似(使用 -fcheck=all 引发运行时错误)。所以这本质上与超出数组边界的问题相同......所以我想我现在理解了这种行为。非常感谢:)
  • @roygvib 是的,我对源分配的 cmets 确实是关于编译时检查。正如您所说,启用运行时检查后,通常会有一系列边界测试。有趣的是,ifort 没有注意到这种边界违规/有另一种方法(其中任何一种都是完全合法的)。
【解决方案2】:

Fortran 标准的编写使您不必担心可分配的内存泄漏。如果操作不是一开始就禁止的,则不应造成任何泄漏。此规则适用于任何可能的符合标准且具有可分配变量的操作。

因此,正如英特尔手册所述,to 参数将首先被释放。

【讨论】:

  • 这可能对某人有用,但 franvescalus 的回答更为严谨。我会离开是作为提供信息。在他的答案出现之前我就开始写了。
  • 非常感谢您的信息。我认为以这种方式不会发生内存泄漏是令人放心的。即使我们在 Fortran 中使用数组指针,也可以假设没有泄漏发生吗?或者它是一个不同的故事,上述安全行为仅适用于可分配数组......? (我猜是后者。)
  • 使用指针导致内存泄漏或程序崩溃的方法有很多种。他们是一个不同的故事。
【解决方案3】:

是否需要解除分配a(:):

更新我误读了你的问题。您在询问TO 部分。

我刚刚用valgrind 进行了尝试,似乎deallocate 不是防止泄漏所必需的。但我可能还是会这样做,只是为了安全起见。

第二个问题:

[我们可以] ...对不同长度的数组使用源分配吗?

根据我信任的Fortran Book,没有:

[The] ... 分配的数组必须与源数组或表达式一致(具有相同的秩和形状)。

再说一次,我的书没有涵盖 2003 年以后的标准,因此 Fortran 2008 或 Fortran 2015 的情况可能会有所不同。

【讨论】:

  • 非常感谢您的信息。是的,我问的是 TO 部分。我检查了 gfortran 的 man page,但没有足够的信息。所以很怕内存泄露……我也找了Intel的page,上面说TO部分被释放了,但是有时候ifort太慷慨了,所以想知道是不是其他编译器的常见行为。跨度>
  • 至于源分配,我也尝试过allocate( buf( 4 ), source= a(1:2) ) 之类的方法,但是两个编译器都抱怨数组的形状不同。有趣的是,如果源代码部分没有使用冒号,编译器为什么不会引发错误...
猜你喜欢
  • 2019-03-18
  • 1970-01-01
  • 1970-01-01
  • 2017-05-21
  • 2014-02-26
  • 2018-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多