【问题标题】:Assignment of allocatable array with itself values分配具有自身值的可分配数组
【发布时间】:2019-10-22 12:27:11
【问题描述】:

我想知道在现代 Fortran 中是否有可能使用它自己或它的一部分来分配一个可分配数组。这是一个简单的例子:

module modu
implicit none

type :: t
  integer :: i
end type

contains

subroutine assign(a,b)
type(t), allocatable, intent(out) :: a(:) 
type(t),              intent(in)  :: b
allocate(a(1))
a(1) = b
end subroutine
end module

!----------------------

program test
use modu
implicit none
type(t), allocatable :: a(:)

allocate(a(1))
a(1)%i = 2
call assign(a, a(1))
print*, a(1)%i
end program

此代码使用 ifort 18 给出正确答案,并使用 gfortran 7.4 返回“分段错误”。

注意:最初的问题有点复杂,因为 call assign(a, a(1)) 应该替换为 call assign(a, a(1)+b) 并正确重载 operator +,但结论(尊重 ifort 和 gfortran)是相同的。

注意:在线程 checking for self-assignment in fortran overloaded assignment 中,@IanH 区分了 call assign(a,a)call assign(a,(a)),但我相信它不能解决这个问题,因为我有可分配的参数。

注意:在线程 Automatic array allocation upon assignment in Fortran 中,@francescalus 解释了内在赋值的自动分配,但我再次相信它不适用于这里。

【问题讨论】:

    标签: fortran variable-assignment allocatable-array


    【解决方案1】:

    这个电话

    call assign(a, a(1))
    

    被 Fortran 的别名规则禁止。您在不同的参数中传递相同的值,pointertarget 都不是,您正在修改其中一个。

    然后,编译器释放a,因为它是intent(out)。这意味着旧的a(1) 不再存在。但是b 仍然指向那里。然后你在其他地方分配一个新的a。然后你尝试在

    中使用b
    a(1) = b
    

    这肯定会失败,因为它指向一些未定义的内存。您对 Intel Fortran 很幸运,但您的代码是非法的。也许 Intell 将新的 a 分配到旧的 a 所在的位置,但这纯粹是运气。

    如果你这样做,它会起作用

    type(t), allocatable, intent(inout) :: a(:) 
    type(t),              value  :: b
    deallocate(a)
    allocate(a(1))
    a(1) = b
    

    我不确定,为什么它会与 intent(out)value 一起崩溃。可能是编译器的问题,报告为bug 92178

    【讨论】:

    • 谢谢,您的回答很有帮助。我选择@francescalus 来解决这个问题,因为他还指出了我无法检测到的与已定义分配的关系。
    • 编译器可能没有问题。代码可能不符合要求。
    • @steve 很明显,这就是我写“可能”的原因。请参阅错误报告中的讨论。这根本不是那么简单。我对我的代码的解释是它符合要求。另请参阅 francescalus 的回答。
    • 我是错误报告中的讨论。 :-)
    • 哎呀,是你!我从来没有意识到。 :) 我以为你在几年 bacj 之前使用了不同的用户名,然后消失了。
    【解决方案2】:

    可以使用同一数组的数组元素分配给可分配数组。例如,对于 a 可分配

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

    有效且具有预期结果。无论a 是内在类型(对于内在赋值)还是派生类型(对于内在或定义的赋值),这都是相同的。在左侧受到影响之前,右侧被视为完全作为表达式进行评估。

    但是,使用call assign(a,a(1)) 与此分配不同。

    您引用的 IanH 回答中的要点与此处相关。定义的赋值类似于call assign(a,(a(1))) 而不是call assign(a,a(1))

    这很重要,因为 Vladimir F 的回答中提到了别名限制,我不会重复。使用call assign(a,(a(1))) 消除了别名,因为(a(1)) 是一个表达式,其值是数组元素而不是数组元素本身。这类似于 Vladimir F 提到的 value 属性,但没有创建可定义的实体。

    定义的赋值精确地使用(rhs) 构造来避免这个别名问题。

    关于您的 gfortran 结果,IanH 创建了一个bug report for GCC 以回应其他问题。这可能与解释您在这里的惊喜有关。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-03-17
      • 2014-09-26
      • 2022-12-05
      • 2020-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多