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

Here, 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 a (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 the subroutine, 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 identical to 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. Declaring a as 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(:,:))

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 12.5.2.1, 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.



Comments

No comments

You may format you comment with Markdown. If your comment is a valid contribution, it may be posted on this page. Your email address will not be made public.