There are instances where one wishes to change the rank (the number of dimensions) of an array as it is passed to a subroutine. Maybe you want to call a library routine that expects to get a vector, but you have the data to be passed stored in a matrix. There should be no problem in principle, as the data for an array — no matter its rank — is stored sequentially in memory (array element order of subscripts along the first dimension varying more quickly). Thus, it should be possible to simply re-interpret the data to a different shape. In practice, one can change the rank of the array by declaring the dummy array with an explicit shape specification. As soon as the dummy array has an explicit shape, one can pass anything that contains enough elements to fill the dummy array, no matter what the rank or shape. The feature is known as array element sequence association and is illustrated in the following example:
program test implicit none integer :: test_array1(10) integer :: test_array2(10,2) test_array1 = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/) test_array2(:,1) = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/) test_array2(:,2) = (/11, 12, 13, 14, 15, 16, 17, 18, 19, 20/) write(*,*) "print_a(test_array1, 10)" call print_a(test_array1, 10) write(*,*) "print_a(test_array2, 10)" call print_a(test_array2, 10) write(*,*) "print_a(test_array2, 20)" call print_a(test_array2, 20) write(*,*) "print_a(test_array2(:,2), 10)" call print_a(test_array2(:,2), 10) write(*,*) "print_m(test_array1, 2,5)" call print_m(test_array1, 2, 5) contains subroutine print_a(a, n) integer, intent(in) :: n integer, intent(in) :: a(n) integer :: i do i = 1, n write(*,*) a(i) end do end subroutine subroutine print_m(a, n, m) integer, intent(in) :: n integer, intent(in) :: m integer, intent(in) :: a(n,m) integer :: i, j do i = 1, n do j = 1, m write(*,*) a(i,j) end do end do end subroutine end program test
There are two alternatives to do basically the same thing, one standard, one not. The first one is known as assumed-size, and looks like this:
subroutine print_a(a, n) integer, intent(in) :: a(*) integer, intent(in) :: n integer :: i do i = 1, n write(*,*) a(i) end do end subroutine
a has rank one, and a size large enough to fit whatever is passed to
the routine. However, the routine does not actually know the size of
size(a) is illegal in this context). There certainly is nothing that ensures
that the size of
a is n, and the compiler has no way of perfoming any checks
on the use of
a. In that sense, assumed-size is not very useful compared to
explicit-shape, except maybe in some rare instances where the array size can
somehow be deduced from the data stored in the array, so that the array size
n does not have to be passed along.
The second alternative is a non-standard trick that behaves identically to assumed-size, but was used before the assumed-size feature was introduced to Fortran. It looks like this:
subroutine print_a(a, n) integer, intent(in) :: a(1) integer, intent(in) :: n integer :: i do i = 1, n write(*,*) a(i) end do end subroutine
In principle, this is also an example of array element sequence association. In
a is called with subscripts outside of the bounds of its
declared size 1, which is more or less guaranteed to work since the array is
passed by reference. Again, the compiler has not idea about the actual size of
a. Most compilers will recognize this trick and not complain about going out
of bounds, even with bound-checking enabled, understanding
a(1) to be
a(*), with the only difference being that
size(a) is legal,
but of course returns 1, i.e. not the size of the actual passed array.
a(2) will not work, however.
Any of these definitions should only be used if the rank of the array needs to
be changed. For just passing arrays of identical rank, but unknown shape, one
should always use the assumed-shape syntax (e.g.
A few pointers about where to find the discussed concepts in the Fortran 90 Handbook: array element order is discussed in section 6.4.7, the definition of array element sequence association appears in section 188.8.131.52, and explicit-shape, assumed-shape, deferred-shape, and assumed-size specification are explained in section 5.3.1.
Thanks to the people on
comp.lang.fortran for illuminating some of these
concepts to me.