Arrays are multidimensional variables that contain more than one value where each value is accessed using one or more indices. First element along any dimension is at index 1 in contrast to C and Java where first element start from 0. Index can't have gaps.
You can declare arrays of any type but all elements in a single array must have the same type. There are three notations for declaring array variables:
1. You can use dimension attribute after type, separated by comma to specify 1 or N fix dimensions in paranthesis. This will reserve the space necesary for multiple elements.
! 1D integer array of 9 elements
integer, dimension(9) :: array_nae
! 2D real array, we call it matrix
real, dimension(3, 3) :: matrix_name
2. You can specify the number of values using round paranthesis after the variable name. This is more intuitive and not so verbose. Whatever method you use, the elements are not initialized.
! An array of 9 real elements
real :: array_name(9)
! A matrix of 9 integer elements
integer :: matrix_name(3,3)
3. You can specify lower and upper bounds for indexes. In special cases this could be useful. This method is rarely used by advanced developers to create custom index that start with other than 1, for example: a yearly schedule.
! Custom lower and upper bounds
real :: zeroid_array(0:9)
! Negative index is also possible
real :: negid_array(-5:5)
You can initialize all the values of one entire array in a single statement. Actually you should do this only if you do not use other method to give initial value to all elements.
Array literal is enclosed in square brackets [...], literal values are separated by comma. The same notation is used for constructors to initialize the array. Notice that [] is not used for index, and array dimension but (). Very strange!
! using array literal
real :: my_array(10) = [1,2,3,4,5,6,7,8,9,10]
To avoid loops for Array initialization we can use a special expression that generate a series of nambers, called range. A range expression is enclosed in round brackets.
!fortran example
program arrays
! 1D integer array
integer, dimension(3) :: A
integer i
! give value to all elements
A=[(i, i=1,3)]
! print all values
print "(3i4)", A(1:3)
end program
~>./arrays
1 2 3
~>
In memory elements are organized linear, columns first. We say that matrix are in column major order, in contrast to C where row major order is used. It is important to use the right order when you travers a 2d array.
Column Major Order
Nested loops can be used to access every element in the matrix. When you do this kind of loops, pay atention to order of indexes. In fortran you must use second index for outer loop and first index for inner loop. For large matrix this can improve performance.
program matrix
! 2D integer array
integer, dimension(3, 3) :: M
! give value to all elements
do j=1,3
do i=1,3
M(i,j)=j*10+i
end do
end do
! output matrix elements
print "(3i4)", M ! 3 columns
print "(9i4)", M ! 9 columns
end program
>./matrix
11 12 13
21 22 23
31 32 33
11 12 13 21 22 23 31 32 33
A powerful feature of the Fortran language is its built-in support for array operations. You can perform operations part of an array using array slicing notation: A(x:y) for array or M(x:y, a:b) for a matrix. Let's analyze the example:
!fortran example
program slices
implicit none
integer :: i, j
integer :: m(9, 9)
! Implied do loop constructor
do i=1,9
m(i,:)=[(j*10+i, j = 1, 9)]
end do
print "(9i4)", m
! Set value elements by range
m(:,:) = 0
m(1:5,1:5) = 1
m(6:,6:) = 2
print *, repeat("_",36)
! Print new values
print "(9i4)", m
end program
~>./slices
11 12 13 14 15 16 17 18 19
21 22 23 24 25 26 27 28 29
31 32 33 34 35 36 37 38 39
41 42 43 44 45 46 47 48 49
51 52 53 54 55 56 57 58 59
61 62 63 64 65 66 67 68 69
71 72 73 74 75 76 77 78 79
81 82 83 84 85 86 87 88 89
91 92 93 94 95 96 97 98 99
__________________________________
1 1 1 1 1 0 0 0 0
1 1 1 1 1 0 0 0 0
1 1 1 1 1 0 0 0 0
1 1 1 1 1 0 0 0 0
1 1 1 1 1 0 0 0 0
0 0 0 0 0 2 2 2 2
0 0 0 0 0 2 2 2 2
0 0 0 0 0 2 2 2 2
0 0 0 0 0 2 2 2 2
Arrays can be defined resizable, then allocate memory later. You can use allocatable attribute in declaration and then allocate function to reserve the required memory.
>program main
implicit none
! define dynamic arrays
integer, allocatable :: darray(:)
integer:: i = 0, d = 0 ! define the dimension
print *, "dimension:", read *, d
allocate(darray(d)) ! resize the arrays
darray = [(i,i=1,d)] ! using constructor
! create a dynamic format and print
print "("//trim(str(d))//"i4)" , darray
deallocate(darray) ! remove from memory
contains
! "Convert an integer to string."
character(len=20) function str(k)
integer, intent(in) :: k
write (str, *) k
str = adjustl(str)
end function
end program
In Fortran, strings are defined using character type, follow (len) argument. An array can be defined as fixed size or allocatable. For allocatable strings you can allocate memory later after you can calculate the required capacity.
>program main
! declare two empty strings
character(len=5) :: string_one
character(len=:), allocatable:: string_two
! allocate memory for string two
allocate(character(6) :: string_two)
string_one = "first"
string_two = "second"
! concatenate and print
print *, string_one //" "// string_two
end program
Fortran has 5 built-in data types that can be used to define derived types. These are special form of data types that can encapsulate multipe values of different built-in types as well as other derived types. Derived types are like struct or classes.
Most of the time a type has only a name and a group of variables called "attributes" or sometimes "fields". In Fortran the "derived types" are similar to "classes" you encounter in OOP languages.
type :: identifier_name
! attributes
...
end type
You can define more advanced data types. We try to show you many optional features by using square brackets []. However in next examples you will realize, square brackets are not actually part of syntax. It just show that a feature is optional.
type ,attribute-list :: identifier_name [("parameter list")]
["parameterized definition"]
["private statements or sequence statements"]
["member variables (fields or attribute)"]
contains
["type bound subprograms"]
end type
In this example we create 2 items. Each item is a variable declared from a derived type "wheel" and initialize the attributes using symbol "%". Notice this symbol is not modulo, like in C. For module we use built-in function mod() instead of "%".
program objects
character(len=*), parameter :: fmt = "(i3,i3,f6.2,' ',a,' ',a)"
! derived data type
type :: wheel
integer :: spokes, diameter
real :: width
character(len=4):: breaks
character(len=15) :: material
end type
! define and create two wheels
type(wheel) :: mountain, road
mountain = wheel(32, 24, 2.2, "disk", "steel")
! second weel attributes
road%diameter=26; road%width=1.5
road%spokes=22; road%breaks="rim"
road%material="aluminium"
! list the two weels with attributes
print fmt, road
print fmt, mountain
end program
~>gfortran wheels.f95 -o wheels
~>./wheels
22 26 1.50 rim aluminium
32 24 2.20 disk steel
~>
Pointers are best thought of as variables which are dynamically reffering to or are associated with a particular target. Pointers are pointing to something. Valid pointer targets include:
Note: Pointers may take advantage of dynamic storage but do not require the allocatable attribute. The ability to allocate and deallocate storage is an native property of pointer variables.
!declare target variable
integer, target :: var
A pointer is not just a number. It is a structure, it has data type and the phisical memory address of the target. Therefore after declare a pointer need to be associated to a target.
!declare a null pointer
integer, pointer :: ptr
! associate pointer to var
ptr => var
A pointer can hold a reference to one variable then switch to another variable. The pointer is used almost like a variable. It can be modified using regular operators. Take a look at example and read the comments:
program point
real, target :: a, b
real, pointer:: pa
if (associated(pa)) then
print *, 'test 1: pa is currently associated'
else
print *, 'test 1: pa is currently not associated'
endif
a = 1.2; b = 2.2
pa => a ! associate pointer pa to variable a
print *, "pa = ", pa
if (associated(pa)) then
print *, 'test 2: pa is currently associated to a'
else
print *, 'test 2: pa is currently not associated'
endif
nullify (pa)
if (associated(pa)) then
print *, 'test 3: pa is currently associated'
else
print *, 'test 3: pa is currently not associated'
endif
pa => b ! associate pointer pa to variable b
if (associated(pa)) then
print *, 'test 4: pa is currently associated to b'
else
print *, 'test 4: pa is currently not associated'
endif
! modify the variable b using it's alias (pa)
pa = pa + 1
print *, "b = ", b
end program
./point
test 1: pa is currently not associated
pa = 1.20000005
test 2: pa is currently associated to a
test 3: pa is currently not associated
test 4: pa is currently associated to b
b = 3.20000005
Read next: Using Modules