5

I have seen several questions about passing unallocated arrays into functions or subroutines that cause runtime faults where the recommended solution was to allocate it in the calling procedure with zero length. I did some experiments:

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  

The first one is obvious. An array of length 3. The second one takes advantage of a cool feature of Fortran, defining your own index limits. It is an array of length 3 with the elements A(-5), A(-4), and A(-3).

Third one gets dicey. I allocate it with a zero. I can assign a value! It prints: 9 Size: 0
The fourth one prints a size of zero too!

Apparently, the correct way to allocate an array of length 1 with an index of -4 is the last one.

Question: On the third and fourth ones, did I just write to memory that likely belongs to some other variable (except of course this example has no other variables)?

Dan Sp.
  • 1,419
  • 1
  • 14
  • 21
  • 2
    You did compile with array bounds checking turned on, didn't you? – Ian Bush Jan 27 '18 at 07:36
  • 1
    *I have seen several questions about passing unallocated arrays into functions or subroutines that cause runtime faults where the recommended solution was to allocate it in the calling procedure with zero length.* Really ? Such as ? I think there might be a more interesting question hidden inside what OP has actually posted. – High Performance Mark Jan 27 '18 at 08:23
  • 1
    Here [link](https://stackoverflow.com/questions/48155025/fortran-function-returning-unallocated-array-causes-segmentation-fault/48467963#48467963) is an example. So on the third one, it is zero sized. I know this. But I stored a value in it and successfully printed the 9 to the screed. What did I just overwrite in memory? Some other variable? – Dan Sp. Jan 27 '18 at 14:11

2 Answers2

2

The third fragment is invalid given the attempt at defining and referencing the element A(0). A zero sized array has no elements (otherwise its size would not be zero) - this includes there being no element that happens to have index zero.

There is no such invalid definition or reference in the fourth fragment. The allocated array again has zero size (which is what the language says happens when you specify an array with a dimension that has an upper bound that is less than the lower bound (the default lower bound is one if otherwise unspecified)), but nothing interesting is then done with that allocated array.

You could construct an equivalent example without using allocatable objects.

IanH
  • 21,026
  • 2
  • 37
  • 59
1

Apart from that the code is invalid, if the question is "whether A(0) is accessing some memory region that should not be accessed", I guess it is very likely so. Although the result is compiler-dependent (and does not prove anything), we can see some info using c_loc, for example,

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  

Then gfortran-7.2 (with no option) gives

 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

and Oracle Studio fortran 12.5 (with no option) gives

 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

where the valid memory region allowed for us (= user) to access is only (*1) and (*2). Since the array elements above are accessed contiguously on memory, the memory location other than (*1) and (*2) may represent some other data/metadata/buffer etc, and anything could happen if we modify those values inadvertently (including WW3 or more recently, huge digital currency theft..?). As well known, we can check this usually with an option like gfortran-7 -fcheck=all, which gives

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

or f95 -C test.f90 (for 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'

We get similar errors for A(0), so it is not allowed to access A(0) (simply because A is an empty array). (The exception to this is when A is accessed via argument or storage association etc, but I guess this is another story...)

roygvib
  • 7,218
  • 2
  • 19
  • 36
  • Thanks for the detailed example. I find it interesting that the zero length array A seems to have been given space between X and B although I know this memory should not be accessed. – Dan Sp. Jan 27 '18 at 19:37
  • I think even if A is of size zero, it needs to be given a "descriptor" (something like a struct in C that contains metadata for an ALLOCATABLE array), which may also be related somehow (but not sure in detail..). http://thinkingeek.com/2017/01/14/gfortran-array-descriptor – roygvib Jan 27 '18 at 19:52