Fortran中赋值时自动分配数组大小

12

我们最近发现在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编译器下在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是否成功。 - stark
我刚刚添加了一个检查,现在malloc在所有情况下都成功了。 - Donna
1
如果数组未分配内存,则 array = x 是非法的。 - stark
就是这样!在编译行中添加“'fno-realloc-lhs”会在所有情况下触发段错误。谢谢。 - Donna
1
根据之前的评论(现在神秘地消失了),GNU 4.6及以上版本允许自动分配LHS数组。 - Donna
2个回答

30

像这样的代码的有效性

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

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

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

从Fortran 2003开始,只需要匹配秩即可。如果存在形状不匹配,则该数组将首先被取消分配,然后重新分配为正确的形状。如果最初未分配,则会进行分配。

因此,上面的程序不是有效的Fortran 90,但是是有效的Fortran 2003。

那么实际代码中的区别在于编译器支持的语言语法。

对于gfortran,Fortran 2003对可分配数组的赋值在4.6、2011-01-28中引入

正如也进行了评论,命令行选项-fno-realloc-lhs1可以禁用此自动(再)分配,使编译器不符合Fortran 2003+的规范。


1其他编译器具有类似的行为:添加所需的检查以确定是否需要重新分配是性能损失,在符合Fortran 90的代码中是多余的,甚至在现代代码中也可能不被许多人使用。例如,在某些支持F2003的Intel编译器版本中,默认情况下会忽略此设置

您可以始终通过使用数组截取来禁止现代代码中数组的(再)分配检查/操作

array(:) = (/1,2,3/)
在这种情况下,为了使赋值语句有效,array(如果是可分配的)必须被分配为大小为3的一维数组。这是在整个数组 array=(/1,2,3/) 被解释为Fortran 90的赋值时的情况。
原因是,在这个脚注的数组部分中,左侧并不是可分配的,即使数组本身是可分配的。

3
原来只有GNU gfortran 4.6及以上版本才允许在F90中自动重新分配LHS数组。使用编译器标志-fno-realloc-lhs会禁用此功能,并在我上述所有情况下(OSX,Linux,PPC)触发seg.故障。谜团解开了!感谢神秘的评论者,他的评论神秘地消失了。
请参见GCC 4.6 Wiki

3
不,F90不允许使用它。如果您使用严格的Fortran 95编译,就不应该使用它。正如francescalus所解释的那样,这是Fortran 2003的功能。如果您指的是在.f90文件中,则可以使用,但这并不意味着是Fortran 90。 - Vladimir F Героям слава
是的 - 我指的是 .f90 文件。我正在使用 gfortran 编译器(gcc5),我认为它包括最新的 f95、2003 年及以后的标准。 - Donna
3
如果代码不符合特定标准的规则,那么这个标准并没有规定会发生什么,这意味着在“正确行为”方面,任何事情都可能发生。 “任何事情”可能包括程序崩溃,程序进行某种魔法重新分配,或程序写一封伪造的辞职信给你的老板,然后偷跑和你的配偶私奔。(实际上可能发生的情况是另一个问题。) - IanH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接