问题描述
我们最近发现我们正在对 Fortran 中的未分配数组进行赋值.GNU gfortran 编译器没有发现错误,代码在 OSX 和 Linux 下运行.然而,同样的代码分段在 IBM Power PC 上会出错.
We recently discovered we were making an assignment to an unallocated array in Fortran. The GNU gfortran compiler didn't catch the error, and the code runs under both OSX and Linux. However, the same code segmentation faults on a IBM Power PC.
我的问题是,下面的代码正确吗?似乎分配给 array
的数组在某些体系结构上自动分配内存,但在其他体系结构上则不然.这里有具体的实现细节吗?
My question is, is the following code correct? It seems that the array assigned to array
is automatically allocating memory, on some architectures, but not on others. Are there implementation specific details at work here?
代码是混合的 C/Fortran 代码:
The code is a mixed C/Fortran code :
#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 代码:
And the Fortran code :
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 上的输出:
And here is the output on the 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(架构:ppc64)上编译时无法运行.在我们的原始代码中,Power PC 实现只是在代码的后期出现了段错误,其中引用了分配给数组的条目,这使得我们的错误"(如果它实际上是一个错误)非常难以追踪.
In summary: It runs when compiled under GNU (GNU Fortran (MacPorts gcc5 5.4.0_0) 5.4.0) gfortran on OSX (arch : x86_64h) and Linux (in a virtual machine hosted on OSX, GNU Fortran (Ubuntu 4.9.4-2ubuntu1~14.04.1) 4.9.4), but fails to run when compiled on the Power PC (arch: ppc64) compiled using GNU Fortran (GCC) 4.4.7 20120313 (Red Hat 4.4.7-17). In our original code, the Power PC implementation only segfaulted much later in the code, where entries assigned array was referenced, making our 'bug' (if it is in fact a bug) really hard to track down.
上述代码的正确行为是什么?
What is the correct behavior for the above code?
推荐答案
代码之类的有效性
integer, allocatable :: array(:)
array = (/1,2,3/)
end
取决于用来解释它的 Fortran 标准.
depends on the standard of Fortran used to interpret it.
Fortran 2003 引入了内部赋值自动分配的概念.在 Fortran 2003 之前,必须分配这种赋值语句左侧的数组,并且必须与右侧的数组具有相同的形状.
Fortran 2003 introduced the concept of automatic allocation on intrinsic assignment. Before Fortran 2003 the array on the left-hand side of such an assignment statement must be allocated, and of the same shape as the array on the right-hand side.
从 Fortran 2003 开始,只有排名需要匹配.如果形状不匹配,数组将首先被释放,然后重新分配到正确的形状.如果最初没有分配,就会分配.
From Fortran 2003 only the rank needs match. If there is a shape mismatch, the array would be first deallocated and then reallocated to the correct shape. If not allocated initially, it would be allocated.
因此,上面的程序不是有效的 Fortran 90,而是有效的 Fortran 2003.
So, the program above isn't valid Fortran 90, but is valid Fortran 2003.
实际代码的不同之处在于编译器支持的语言语法.
The difference in the real-world code, then, is from what language syntax the compilers support.
对于 gfortran,Fortran 2003 对可分配数组的分配是 在 4.6, 2011-01-28 中引入一>.
For gfortran, Fortran 2003's assignment to an allocatable array was introduced in 4.6, 2011-01-28.
正如所评论的,命令行选项 -fno-realloc-lhs
禁用此自动(重新)分配,使编译器不符合 Fortran 2003+.
As also commented, the command line option -fno-realloc-lhs
disables this automatic (re-)allocation, making the compiler not Fortran 2003+ compliant.
其他编译器也有类似的行为:添加必要的检查以检查是否需要重新分配会影响性能,这在符合 Fortran 90 的代码中是多余的,并且可能是许多人甚至在现代中都不使用的功能代码.例如,对于 Intel 的编译器,在某些支持 F2003 的版本中,默认情况下会忽略这一点.
Other compilers have similar behaviour: adding a required check for whether reallocation is necessary is a performance hit which is redundant in Fortran 90 compliant code and may be a feature not used by many even in modern code. For Intel's compiler, for example, in some versions which do support F2003 the default is to ignore this.
人们总是可以通过使用数组部分来抑制现代代码中数组的(重新)分配检查/操作
One can always suppress the (re-)allocation checks/actions of an array in modern code by using an array section
array(:) = (/1,2,3/)
在这种情况下,必须分配 array
(如果可分配),等级为 1,大小为 3,赋值语句才有效.这就像在 Fortran 90 解释下对整个数组 array=(/1,2,3/)
的赋值一样.
In this case, array
(if allocatable) must be allocated, of rank 1 and of size 3 for the assignment statement to be valid. This is as would be under a Fortran 90 interpretation of the assignment with the whole array array=(/1,2,3/)
.
这样做的原因是,对于此脚注的数组部分,即使数组本身是可分配的,左侧也是不可分配的.
The reason for this is that with the array section of this footnote, the left-hand side is not allocatable, even though the array itself is.
这篇关于在 Fortran 中分配时自动分配数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!