Reputation: 1447
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)?
Upvotes: 5
Views: 2423
Reputation: 7385
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...)
Upvotes: 1
Reputation: 21431
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.
Upvotes: 2