Fortran中的零大小数组到底是什么情况?

5

我看到了一些与将未分配的数组传递给函数或子程序有关的问题,这可能会导致运行时错误,而建议的解决方案是在调用过程中分配长度为零的数组。我进行了一些实验:

program test
   integer, allocatable :: A(:)

   allocate(A(3))
   print*, size(A)
   deallocate(A)

   allocate(A(-5:-3))
   print*, size(A)
   deallocate(A)

   allocate(A(0))
   A(0) = 9
   print*,A(0),'Size:',size(a)
   deallocate(a)

   allocate(A(-4))
   print*,size(A)
   deallocate(A)

   allocate(A(-4:-4))
   print*,size(A)
end  

第一个很明显,长度为3的数组。第二个利用了Fortran的一个很酷的特性,定义自己的索引限制。它是一个长度为3的数组,元素为A(-5),A(-4)和A(-3)。
第三个有点棘手。我用零分配了它。我可以赋值!它打印出:9大小:0
第四个也打印出大小为零!
显然,正确分配长度为1且索引为-4的数组的方法是最后一个。
问题:在第三个和第四个中,我是否刚刚写入了可能属于某些其他变量(当然,此示例没有其他变量)的内存?

2
你编译时开启了数组边界检查,对吧? - Ian Bush
1
我看到过一些关于将未分配的数组传递到函数或子程序中导致运行时错误的问题,推荐的解决方案是在调用过程中分配长度为零的数组。真的吗?比如哪些问题?我认为OP实际发布的内容中可能隐藏着一个更有趣的问题。 - High Performance Mark
1
这里是一个例子。在第三个中,它的大小为零。我知道这一点。但我存储了一个值,并成功地将9打印到屏幕上。我刚刚覆盖了内存中的什么?其他变量吗? - Dan Sp.
2个回答

2
第三个片段尝试定义和引用元素 A(0),这是无效的。一个大小为零的数组没有元素(否则它的大小就不会是零),这包括没有一个元素具有索引零。
第四个片段中没有这样的无效定义或引用。分配的数组再次具有零大小(当您指定一个维度上限小于下限时(默认下限为1,如果未另行指定),语言规定其大小为零),但是对该分配的数组没有进行任何有趣的操作。
您可以构建一个等效的示例,而不使用可分配对象。

1
除此之外,如果问题是“是否A(0)正在访问不应访问的某个内存区域”,我认为很有可能是这样。虽然结果取决于编译器(并且无法证明任何事情),但我们可以使用c_loc查看一些信息,例如,
program test
    use iso_c_binding, only: c_loc
    implicit none
    integer, target :: i
    integer, allocatable, target :: X(:), A(:), B(:)

    allocate( X( 1 ), A( 0 ), B( 1 ) )

    print *, "X:"
    do i = 0, 2
        print *, i, c_loc( X( i ) ), X( i )
    enddo

    print *, "A:"
    do i = 0, 2
        print *, i, c_loc( A( i ) ), A( i )
    enddo

    print *, "B:"
    do i = 0, 2
        print *, i, c_loc( B( i ) ), B( i )
    enddo
end  

然后,gfortran-7.2(没有选项)会给出以下结果:
 X:
           0      140362656006716           0
           1      140362656006720           0   <-- (*1)
           2      140362656006724 -1879048192
 A:
           0      140362656006732 -1879048192
           1      140362656006736           0
           2      140362656006740 -1879048192
 B:
           0      140362656006748 -1879048192
           1      140362656006752           0   <-- (*2)
           2      140362656006756 -1879048192

Oracle Studio Fortran 12.5(无选项)提供了

 X:
 0 20258316 0
 1 20258320 0  <-- (*1)
 2 20258324 0
 A:
 0 20258348 0
 1 20258352 0
 2 20258356 0
 B:
 0 20258380 0
 1 20258384 0  <-- (*2)
 2 20258388 0

在编程中,我们能够访问的有效内存区域仅限于 (*1) 和 (*2)。由于数组元素在内存中是连续访问的,因此除了 (*1) 和 (*2) 之外的内存位置可能代表其他数据/元数据/缓冲区等,如果我们无意中修改这些值,可能会发生任何事情(包括第三次世界大战或最近的巨额数字货币盗窃)。众所周知,通常可以使用诸如 gfortran-7 -fcheck=all 的选项进行检查。

 X:
At line 11 of file test.f90
Fortran runtime error: Index '0' of dimension 1 of array 'x' below lower bound of 1

或者 f95 -C test.f90(适用于Oracle)

 X:
 ******  FORTRAN RUN-TIME SYSTEM  ******
Subscript out of range. Location:  line 11 column 31 of 'test.f90'
Subscript number 1 has value 0 in array 'X'

我们对于 A(0) 也会得到类似的错误,因此无法访问 A(0)(因为 A 是一个空数组)。(唯一的例外是当通过参数或存储关联等方式访问 A 时,但我想这是另一回事...)

感谢提供详细的示例。我发现有趣的是,尽管我知道不应该访问这段内存,但零长度数组A似乎已经在X和B之间分配了空间。 - Dan Sp.
我认为即使A的大小为零,它也需要被赋予一个"描述符"(类似于C语言中包含ALLOCATABLE数组元数据的结构体),这可能也与某些相关(但不确定详细情况...)。http://thinkingeek.com/2017/01/14/gfortran-array-descriptor - roygvib

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