【问题标题】:Automatic array allocation upon assignment in Fortran在 Fortran 中分配时自动分配数组
【发布时间】:2017-02-09 15:37:57
【问题描述】:

我们最近发现我们正在对 Fortran 中的未分配数组进行赋值。 GNU gfortran 编译器没有发现错误,代码在 OSX 和 Linux 下都运行。但是,IBM Power PC 上出现相同的代码分段错误。

我的问题是,以下代码是否正确?似乎分配给array 的数组在某些架构上会自动分配内存,而在其他架构上则不会。这里有具体的实现细节吗?

代码是混合的 C/Fortran 代码:

#include <stdlib.h>

void assign_array_(double x[], int* n);
void print_array_();

int main()
{
    int n,i;
    double *x;

    n = 5;
    x = (double*) malloc(sizeof(double)*n);

    for (i = 0; i < n; i++)
        x[i] = (double) i;

    assign_array_(x,&n);
    print_array_();

    return 0;
}

还有 Fortran 代码:

MODULE test_mod
  DOUBLE PRECISION, ALLOCATABLE, DIMENSION(:) :: array
  integer :: nsize
END MODULE test_mod

SUBROUTINE assign_array(x,n)
  USE test_mod
  IMPLICIT NONE

  INTEGER :: n
  DOUBLE PRECISION :: x(n)

  CALL test_allocated()
  array = x
  CALL test_allocated()
  nsize = n

END SUBROUTINE assign_array


SUBROUTINE print_array()
  USE test_mod, ONLY: nsize, array
  IMPLICIT NONE

  INTEGER :: i

  DO i = 1,nsize
     WRITE(6,'(F24.16)') array(i)
  END DO

END SUBROUTINE print_array

SUBROUTINE test_allocated()
  USE test_mod
  IMPLICIT NONE

  IF (ALLOCATED(array)) THEN
     WRITE(6,*) 'Array is allocated'
     WRITE(6,*) 'size is ', SIZE(array)
  ELSE
     WRITE(6,*) 'Array is NOT allocated'
  END IF
END SUBROUTINE test_allocated

输出(运行时)是:

Array is NOT allocated
Array is allocated
size is            5
  0.0000000000000000
  1.0000000000000000
  2.0000000000000000
  3.0000000000000000
  4.0000000000000000

这是 Power PC 上的输出:

Array is NOT allocated
Segmentation fault (core dumped)

总结:它在 GNU (GNU Fortran (MacPorts gcc5 5.4.0_0) 5.4.0) gfortran on OSX (arch : x86_64h) 和 Linux(在 OSX、GNU Fortran (Ubuntu 4.9 .4-2ubuntu1~14.04.1) 4.9.4),但在使用 GNU Fortran (GCC) 4.4.7 20120313 (Red Hat 4.4.7-17) 编译的 Power PC (arch: ppc64) 上编译时无法运行。在我们的原始代码中,Power PC 实现只是在代码的更晚部分出现了段错误,其中引用了分配数组的条目,这使得我们的“错误”(如果它实际上是一个错误)真的很难追踪。

上述代码的正确行为是什么?

【问题讨论】:

  • 你没有检查 malloc 是否成功。
  • 我刚刚添加了一个检查,malloc 在所有情况下都成功。
  • 如果数组没有分配,那么array = x是非法的。
  • 就是这样!在所有情况下,将'fno-realloc-lhs 添加到编译行都会触发 seg.fault。谢谢。
  • @stark 根据之前的评论(现在神秘消失了)GNU 4.6 及更高版本允许自动分配 LHS 数组。

标签: arrays memory fortran gfortran


【解决方案1】:

类似代码的有效性

integer, allocatable :: array(:)
array = (/1,2,3/)
end

取决于用于解释它的 Fortran 标准。

Fortran 2003 引入了内在赋值自动分配的概念。在 Fortran 2003 之前,必须分配此类赋值语句左侧的数组,并且必须与右侧数组的形状相同。

从 Fortran 2003 开始​​,只有等级需要匹配。如果形状不匹配,数组将首先被释放,然后重新分配到正确的形状。如果一开始没有分配,就会被分配。

所以,上面的程序不是有效的 Fortran 90,而是有效的 Fortran 2003。

因此,实际代码的区别在于编译器支持的语言语法。

对于 gfortran,Fortran 2003 对可分配数组的分配是 introduced in 4.6, 2011-01-28

正如还评论的那样,命令行选项 -fno-realloc-lhs1 禁用此自动(重新)分配,使编译器不兼容 Fortran 2003+。


1 其他编译器也有类似的行为:为是否需要重新分配添加必要的检查是性能损失,这在符合 Fortran 90 的代码中是多余的,并且可能是许多人甚至在现代环境中都没有使用的功能代码。例如,对于Intel's compiler,在某些支持 F2003 的版本中,默认情况下会忽略它。

人们总是可以通过使用数组部分来抑制现代代码中数组的(重新)分配检查/操作

array(:) = (/1,2,3/)

在这种情况下,必须分配array(如果可分配),等级为 1,大小为 3,赋值语句才有效。这与 Fortran 90 对整个数组 array=(/1,2,3/) 的赋值解释相同。

原因在于,对于本脚注的数组部分,左侧不可分配,即使数组本身是可分配的。

【讨论】:

    【解决方案2】:

    事实证明,只有 GNU gfortran 4.6 及更高版本允许在 F90 中自动重新分配 LHS 数组。使用编译器标志 -fno-realloc-lhs 会禁用此功能并触发 seg。我上面描述的所有情况下的故障(OSX,Linux,PPC)谜团解决了!感谢神秘海报的评论神秘地消失了。

    GCC 4.6 Wiki

    【讨论】:

    • 不,他们不允许在 F90 中使用它,如果您使用严格的 Fortran 95 进行编译,则不应使用它。这是一个 Fortran 2003 功能,正如 francescalus 所解释的那样。如果您的意思是在 .f90 文件中,那么可以,但这并不意味着 Fortran 90。
    • 是的 - 我的意思是 .f90 文件。我正在使用gfortran 编译器 (gcc5),我假设它包括最新的 f95、2003 和更高标准。
    • 一个区别——如果代码不符合特定标准的规则,那么标准没有规定会发生什么,这意味着就“正确的行为”而言——任何事情都可能发生。 “任何事情”可以包括程序崩溃,或者程序进行某种神奇的重新分配,或者程序给你的老板写了一封假的辞职信然后和你的配偶私奔。 (实际可能发生的情况是另一个问题。)
    猜你喜欢
    • 2014-02-28
    • 1970-01-01
    • 2019-12-27
    • 1970-01-01
    • 2012-06-28
    • 2018-09-01
    • 1970-01-01
    • 2015-05-26
    • 1970-01-01
    相关资源
    最近更新 更多