Intro f90 Notes
Intro f90 Notes
Student Notes
Rob Davies
Cardiff
Alan Rea
Belfast
Dimitris Tsaptsinos
SEL - HPC
Cardiff HPC Training
& Education Centre
Version 1.0
9 Introduction
9 Programming in general
9 History
10 ANSI Standard
10 Compilation
11 Coding conventions
23 Character Processing
23 Character Type
23 Character Constants
24 Character Variables
24 Character manipulation
24 Concatenation
25 Substrings
25 Intrinsic Functions
26 Exercises
35 Arrays
35 Terminology
35 Arrays and elements
36 Array properties
36 Specifications
37 Array Sections
37 Individual elements
38 Sections
39 Vector Subscripts
39 Array storage
40 Array Assignment
40 Whole array assignment
40 Array section assignment
41 Renumbering
41 Elemental intrinsic procedures
41 Zero-sized arrays
42 Arrays and derived types
43 Initialising arrays
43 Constructors
43 Reshape
44 DATA statement
44 WHERE
45 Array intrinsic functions
45 Example of reduction
46 Example of inquiry
46 Example of construction
46 Example of location
47 Exercises
51 Control statements
51 Conditional statements
51 IF statement and construct
53 SELECT CASE construct
53 GOTO
54 Repetition
54 DO construct
55 Transferring Control
56 Nesting
56 Exercises
59 Program units
59 Program structure
60 The main program
60 Procedures
61 Actual and dummy arguments
62 Internal procedures
62 External procedures
63 Procedure variables
88 READ Statement
89 WRITE Statement
89 OPEN Statement
90 CLOSE statement
90 INQUIRE statement
91 Exercises
93 Dynamic arrays
93 Allocatable arrays
93 Specification
93 Allocating and deallocating storage
94 Status of allocatable arrays
95 Memory leaks
96 Exercises
97 Pointer Variables
97 What are Pointers?
97 Pointers and targets
97 Specifications
98 Pointer assignment
99 Dereferencing
100 Pointer association status
100 Dynamic storage
101 Common errors
101 Array pointers
103 Derived data types
103 Linked lists
103 Pointer arguments
104 Pointer functions
106 Exercises
1 Introduction
This course is designed for beginning programmers who may have little or no experi-
ence of computer programming and who wish to take advantage of the new Fortran
standard.
1.1 History
Fortran (mathematical FORmula TRANslation system) was originally developed in
1954 by IBM. Fortran was one of the first languages to allow the programmer to use
higher level (i.e. architecture independent) statements rather than a particular
machine’s assembly language. This resulted in programs being easier to read, under-
stand and debug and saved the programmer from having to work with the details of
the underlying computer architecture.
In 1958 the second version was released with a number of additions (subroutines,
functions, common blocks). A number of other companies then started developing
their own versions of compilers (programs which translate the high level commands
to machine code) to deal with the problem of portability and machine dependency.
In 1962 Fortran IV was released. This attempted to standardize the language in order
to work independent of the computer (as long as the Fortran IV compiler was availa-
ble!)
In 1966 the first ANSI (American National Standards Institute) standard (Fortran 66)
was released which defined a solid base for further development of the language.
In 1978 the second ANSI standard (Fortran 77) was released which standardized
extensions, allowed structured programming, and introduced new features for the IF
construct and the character data type.
The third ANSI standard (Fortran 90) was released in 1991, with a new revision
expected within 10 years.
1.3 Compilation
Once the Fortran 90 program has been designed and entered as source code into a file
(usually with the suffix .f90) then the following steps are possible:
Source code
Compiler
Assembly code
Assembler
Libraries
Object code
Link editor
Executable code
f90 filename.f90
(or something similar) its purpose is to translate the high-level statements
(source code) into intermediate assembly code, and from there to machine
(object) code. The compiler checks the syntax of the statements against the
standard (write rather than write will give an error) and the semantics of the
statements (misuse of a variable, etc.). This step generates the object code ver-
sion which is stored in a file of the same name but different extension (usually o
on UNIX systems).
• Linking - This might be initiated by the compiler, its purpose is to insert any
code that is required from a library or other pre-compiled file. This generates the
executable code version which again is stored in a file with a different extension
(on a UNIX system the default name is a.out).
• Execution - This is initiated by the programmer/user, by typing the name of the
executable file. Its purpose is to run the program to get some answers. During
execution the program might crash if it comes across an execution error (the
most common execution error is an attempt to divide by zero).
Note that logical errors (multiply rather than add) can not be checked by the compiler
and it is the responsibility of the programmer to identify and eliminate such errors.
One way to do so is by testing against data with known results but for more complex
programs testing can not take into consideration all possible combinations of inputs
therefore great care must be taken during the initial design. Identifying errors at the
design phase is cheaper and easier.
PROGRAM hi
! display a message
WRITE(*,*) 'Hello World!'
END PROGRAM hi
As an aid to following the code examples, the convention followed throughout these
notes (recommended by NAG) is:
• All keywords and intrinsic procedure names (i.e. those commands and func-
tions that are a part of the standard) are in upper case, everything else is in lower
case.
• To help with the reading of code, the body of program units are indented by two
columns, as are INTERFACE blocks, DO loops, IF blocks, CASE blocks, etc.
• The name of a program, subroutine or function is always included o their END
statements.
• In USE statements, the ONLY clause is used to document explicitly all entities
which are accessed from that module.
• In CALL statements and function references, argument keywords are always
used for optional arguments.
2.1 Variables
It is usual for people to associate a name or phrase with a piece of information. For
example, the phrase “today’s date” has an associated numeric value which varies day
by day. This is similar to the concept of a program variable; a program variable is
some name (chosen by the programmer) which uniquely identifies an object (piece of
data) stored in memory.
For example, a program may identify the following values:
7
96.4
3.14159
daysinweek
temperature
pi
It is common for the value of a variable to change while a program runs but it is not
required (e.g. the value of temperature might well change but pi is unlikely to).
Variable names are usually a word, phrase, acronym, etc. which is written as one
word (see Naming Convention below). Note that it is good programming practice to
use variable names that are reminiscent of the information being referred to.
It is important for a computer to identify the data type it has stored. There are several
forms of numeric data, namely:
• Integers: may only have discrete, whole values (e.g. -3124, -960, 10, 365, etc.).
• Reals: may have a fractional part and have a greater range of possible values (e.g.
10.3, -8.45, 0.00002, etc.).
• Complex numbers: have both a real and imaginary part (e.g. 3-2i, -5+4i, etc.).
Integers are more accurate for discrete values and are processed fastest, but reals are
necessary for many calculations. Complex numbers are necessary for some scientific
applications.
As well as numerical data, Fortran programs often require other types of data. Single
letters, words and phrases may be represented by the character data type, while the
logical values ‘true’ and ‘false’ are represented by the logical data type (details later).
Finally, It is possible not to use a variable to represent data but to used the value
explicitly. For example, instead of using pi, a programmer might choose to write
3.14159. Such values are known as literal constants.
declares five variables, two which have values that are real numbers and three that
have integer values.
The variable declaration statement may also be used to assign an initial value to vari-
ables as they are declared. If an initial value is not assigned to a variable it should not
be assumed to have any value until one is assigned using the assignment statement.
REAL :: temperature=96.4
INTEGER :: days=365, months=12, weeks=52
INTEGER
REAL
COMPLEX
CHARACTER
LOGICAL
CHARACTER and LOGICAL data types are discussed in separate sections, while the
attributes will be described as required throughout these notes.
2.2.1 Parameters
The term parameter in Fortran is slightly misleading, it refers to a value which will
not change during a program’s execution. For example the programmer is likely to
want the value of pi to be unaltered by a program. Therefore pi may be defined:
REAL specifies the data type while the PARAMETER attribute further defines the varia-
ble pi. All parameters must be given a value in their declaration statement (in this
case 3.141592). Parameters may also be defined for other data types, for example:
IMPLICIT NONE
at the start of each program. This forces a programmer to declare all variables that are
used, and means that some potential errors may be identified during compilation.
If implicit typing is permitted then variables are have a data type according to the ini-
tial letter of their name: those beginning with I, J, K, L, M and N being integers; and
those beginning A to H and O to Z being reals.
For Example:
Both INTEGER, and LOGICAL data types have several possible KIND type values, each
of which uses less memory than the default (which in the case of INTEGER types leads
to smaller possible range of values - so beware!). These alternative KIND values are
usually used only when data storage is at a premium. It is unusual for the CHARAC-
TER data type to have more than one KIND value.
The REAL (and COMPLEX) data type has two KIND values. The default (KIND=1) has a
lower level of precision than (KIND=2). (The two values of KIND are analogous to For-
tran 77’s single and double precision variables.) It is common place to use a REAL of
KIND=2 to store higher levels of precision and/or when a large range of values are
anticipated, i.e.:
The exact level of precision and possible range of values can be checked by using
intrinsic functions (these are self contained, often used blocks of code included in the
language to help the programmer). The intrinsic functions RANGE(), HUGE(), PRE-
CISION(), etc. all give information about the limits of variables of the different KIND
types. Below are some examples from the NAG compiler:
2.3.1 Portability
The number and value(s) of all KIND parameters depend on the compiler being used.
You should be aware that explicitly using a value (like (KIND=3) ) in a program may
not work (or worse give unexpected errors) when using different compilers. For
example some compilers use KIND=1,2 and 3 for the INTEGER data type while oth-
ers use KIND=1,2 and 4.
One way around having to edit programs for different KIND values is to use the
SELECTED_REAL_KIND() and SELECTED_INTEGER_KIND() intrinsic functions.
For integers SELECTED_INTEGER_KIND() is supplied with the maximum exponent
of the data to be stored (i.e 10r where r is the range), the function returns the associ-
ated KIND type parameter for that compiler. For reals SELECTED_REAL_KIND() is
supplied with both the range and the decimal precision and again returns the associ-
ated KIND value.
If the compiler cannot support the requested range and/or level of precision the
SELECTED_type_KIND() functions return a value of -1, and the program will not
compile.
Beware! When converting data from one type to another the variable receiving the
data must be capable of storing the value (range and/or precision). If not error can
occur:
The arithmetic expression may also include brackets which should be used to clarify
the required sequence of operations in an expression. For example:
y = 1+x/2
might be interpreted as either ‘add 1 to x and divide the result by 2’ or ‘add one to half
of x’. The use of brackets can make this clear:
y = 1+(x/2)
y = (1+x)/2
Any expression which appears inside brackets is always evaluated first. In expres-
sions which contain more than one operator the operations are carried out (working
from the left to right in the expression) in an order which is determined by the operator
precedence. Operators are evaluated in the following order:
• Bracketed expressions, (...).
• Exponentiation, **.
• Multiplication and/or division, * or /.
• Addition and/or subtraction, + or -.
Operators of equal precedence are evaluated working from left to right across the
expression, e.g.
2.5 Comments
All programs should have a textual commentary explaining the structure and mean-
ing of each section of the program. All characters appearing on a line to the right of
the ! character are ignored by the compiler and do not form any part of the executable
program. The text appearing after a ! character is referred to as a comment and this
feature should be used to explain to the reader of a program what the program is try-
ing to achieve. This is particularly important if the program will have to be altered
sometime in the future.
Comments are also used to inhibit the action of statements that are used to output
intermediate values when testing a program but which may be required again. The
following statement is said to be ‘commented out’ and is not executed.
PROGRAM circle_area
IMPLICIT NONE
!reads a value representing the radius of a circle,
!then calculates and writes out the area of the circle.
• The program starts with a program statement in which the program is given a
name, i.e. circle_area. The program is terminated with an END PROGRAM
statement (which is also named). All statements in the program are indented to
improve readability.
• There is an explanation of the program, both at the start and in the main body of
code, in the form of comment statements. Again this improves readability.
• The IMPLICIT NONE statement comes first in the program followed by variable
declarations. The variable declarations are grouped together and appear before
all executable statements such as assignments statements and input/output
statements.
• Blank lines are used to emphasize the different sections of the program, again
for readability.
In general programs should be laid out with each statement on one line. However,
there is an upper limit of 132 characters per line, (depending on the editor used it is
often more convenient to keep to a maximum of 80 characters per line) a statement
which exceeds the line limit may be continued on the next line by placing an amper-
sand & at the end of the line to be continued. The line should not be broken at an arbi-
trary point but at a sensible place.
More than one statement may be placed on one line using a semicolon(;) as a state-
ment separator.
This is not recommended as it can lead to programs which are difficult to read - a
statement may be overlooked.
radius
area
A programmer may define special data types, known as derived data types, to create
aggregate structures. A circle could be modelled as follows:
TYPE circle
INTEGER :: radius
REAL :: area
ENDTYPE circle
This would create a template which could be used to declare variables of this type
A derived data type may be constructed from any number or combination of the
intrinsic data types (or from other already defined derived data types). The general
form of the TYPE statement is:
TYPE :: name
component definition statements
...
END TYPE [name]
Note that the type name is optional on the ENDTYPE statement but should always be
included to improve program clarity.
Just like the intrinsic data types, the components of a derived data type may be given
an initial value. For example:
The derived type is so named because it is derived from the intrinsic types, such as
REAL and INTEGER. However derived types may be used in the definition of other
derived types. For example, if a type, point, is defined by:
TYPE point
REAL :: x, y
ENDTYPE point
then the previously defined type, circle, could be modified to include a spacial
position:
TYPE circle
TYPE (point) :: centre
INTEGER :: radius
REAL :: area
ENDTYPE circle
cir_a%radius = 10.0
cir_a%area = pi * cir_a%radius**2
If a derived type has an element which is a derived type then a component may be
accessed as follows:
cir_a%position%x = 5.0
cir_a%position%y = 6.0
Components of a derived type may appear in any expressions and statements that
their data type allow. It is also possible to assign one instance of a derived type to
another instance of the same derived type. The = operator is the only operator that
may be applied to a derived type variable, all other operations require the program-
mer to explicitly access component by component.
cir_a%radius = cir_b%radius
cir_a%area = cir_b%area
cir_a%position%x = cir_b%position%x
cir_a%position%y = cir_b%position%y
cir_a = cir_b !shorthand for all the above
2.8 Exercises
1. Write a program which declares variables to hold the following data:
(a) an integer set to zero.
(b) an integer set to minus one.
(c) 64.0
(d) -1.56x1012 (this should be written as -1.56E12)
Check the program by writing out variables to the screen.
2. Which of the following are invalid names in Fortran 90 and why?
i + j + k * i
z * x / 10 + k
z * k + z * j + z * i
i * y - k / x + j
x / i / z
4. Write definitions of derived types, together with initial values, which represent
the following:
(a) a point with x, y and z coordinates.
(b) a time in hours, minutes and seconds.
(c) a date in day, month and year.
(d) a time comprised of the two types above.
5. Write a program which will read in two real numbers representing the length
and breadth of a rectangle, and will print out the area calculated as length times
breadth. (Use a derived type to represent the rectangle and its area.)
6. Write a program which will read in five integers and will output the sum and
average of the numbers.
Note: the values held by a program variable can be read from and written to
the screen using the READ() and WRITE() statements (which are explained
later in the course), i.e.
3 Character Processing
The characters which appear between pairs of apostrophes are character constants
and will appear on screen as
The double quote character may also be used to define character literals. If a string of
characters is to contain one of the delimiting characters (apostrophes or double
quotes) then the other may be used. However if the string is to contain both delimit-
ing characters or a programmer wishes to always define strings using the same char-
acter then the delimiter may appear in a string as two adjacent apostrophes or double
quotes. These are then treated as a single character.
A value may be assigned to a character variable in the form of a literal constant thus
yesorno = ‘N’
sex = ‘F’
However character variables are more frequently used to store multiple characters
known as strings. For example to store a person’s name the following declarations
and assignments may be made
Notice that all the strings were defined as being long enough to contain the literal con-
stants assigned. Variables which have unused characters are space filled at the end. If
the variable is not large enough to contain the characters assigned to it then the left-
most are used and the excess truncated, for example
title = ‘Professor‘
would be equivalent to
title = ‘Profes‘
As with character literals if the expression using the // operator exceeds the length of
the variable the right-most characters are truncated and if too few characters are spec-
ified the right-most characters are filled with spaces.
3.4.2 Substrings
As the name suggests substrings are sections of larger character strings. The charac-
ters in a string may be referred to by position within the string starting from character
1 the left-most character.
Fortran
INTEGER :: i
CHARACTER :: ch
...
i=ICHAR(CHAR(i))
ch=CHAR(ICHAR(ch))
3.5 Exercises
1. Given the following variable declaration and initialization:
CHARACTER(len=5) :: vowels=‘aeiou‘
what are the substrings specified below?
(a) vowels(1:1)
(b) vowels(:2)
(c) vowels(4:)
(d) vowels(2:4)
2. Given the following variable declaration and initialization:
Logical variable may be assigned a value either explicitly or via a logical expression,
for example:
if today_date has previously been assigned a value and that value is 5 then date
holds .TRUE., otherwise .FALSE.. The relational operator == may be read as ‘equal
to’, thus today_date==5 is read as ‘is today_date equal to 5?’. Below are a list of
the relational operators together with their meaning:
LOGICAL :: test
INTEGER :: age, my_age
CHARACTER(LEN=5) :: name
...
test = 5 < 6 !True
test = 5 > 6 !False
test = 5 == 6 !False
test = 5 /= 6 !True
test = 5 <= 6 !True
test = 5 >= 6 !False
...
test = age > 34 !a variable compared with a constant
test = age /= my_age !two variables are compared
There are two sub-expressions here, one .TRUE. the other .FALSE. hence the result
is .FALSE..
The logical union operator .OR. requires two expressions or variables and gives the
value .TRUE. if either one of the expressions is true, and .FALSE. otherwise. Con-
sider the following example:
LOGICAL :: test
CHARACTER(LEN=10) :: name = ‘James’
...
test = (name='Dimitris') .OR. (name='James') !test=.true.
The logical negation operator .NOT. is used to invert the logical value of an expres-
sion, i.e. .TRUE. becomes .FALSE. and vice versa. For example:
where the statement enclosed by the (optional) brackets is assigned a value which in
turn is inverted.
The logical equivalence operator .EQV. is used to check if all variables or expressions
have the same logical value (can be either .TRUE. or .FALSE.). If both values are the
same the whole expression evaluates to .TRUE., otherwise it evaluates to .FALSE..
For example:
LOGICAL :: test
...
test = (5*3>12) .EQV. (6*2>8) !test=.true.
test = (5*3<12) .EQV. (6*2<8) !test=.true.
LOGICAL :: test
...
test = (5*3>12) .NEQV. (6*2>13) !test=.true.
test = (5*3>12) .NEQV. (6*2<13) !test=.false.
the first expression has one true and one false component and therefore evaluates to
.TRUE., the second expression has two true components and therefore evaluates to
.FALSE..
When comparing REAL with INTEGER values the compiler will convert the integer to
type REAL. Comparing REAL with REAL values must be performed with caution;
rounding errors in the precision of a REAL variable may mean that two REAL numbers
should never be equated. It is advisable to test their difference rather than their actual
values, i.e.
REAL :: a=100.0, b
LOGICAL :: equal
b = 2*50.0
equal = (a==b) !potential error
equal = (a-b)<0.0005 !good programming practice
The right string is shorter, hence 'Alex' becomes 'Alex ' The first 4 letters are the same
- no difference has been found so search continues character i is greater than ‘blank’ -
comparison terminates and the result is .TRUE. because the blank comes before the
letter i in the collating sequence!
Because the collating sequence might differ from machine to machine the above
intrinsic functions (based on the ASCII collating sequence) should be used to compare
strings. More intrinsic functions are available. For example intrinsic functions that
identify the position of a character in a sequence in the ASCII or machine collating
sequence. Some of them are presented through the exercise sections.
4.4 Exercises
1. Given the values below, what is the value of each of the following expressions?
Write a program to test your answers.
15 > 23
(12+3) <= 15
(2>1) .AND. (3<4)
((3>2) .AND. (1+2)<3) .OR. (4<=3)
((3>2) .AND. (1+2)<3) .EQU. (4<=3)
4. Determine the logical value of each of the following expressions. Write a pro-
gram to test your answers.
5 Arrays
5.1 Terminology
5.1.1 Arrays and elements
Previous chapters introduced simple data types, such as INTEGER, REAL and CHAR-
ACTER, and derived data types. In this chapter a means of organising and structuring
data called an array is introduced. An array is a collection of data, all of the same type,
whose individual elements are arranged in a regular pattern.
There are 3 possible types of arrays (based on how the array is to be stored in mem-
ory):
• Static arrays - are the most common type and have their size fixed when the ar-
ray is declared. The number of elements in the array cannot be altered during
the program’s execution. This can be inflexible in certain circumstances (to
change the array sizes parts of the program must be edited and the whole re-
compiled) and can be wasteful in terms of storage space (since the largest possi-
ble array sizes might be declare and may be only partly used).
• Semi-dynamic arrays - have their size determined on entering a sub-program.
Arrays can be created to match the exact size required but can only be used with-
in that particular sub-program. In Fortran 90 such arrays are either assumed
shape, or automatic arrays.
• Dynamic arrays - the size and therefore the amount of storage used by a dynam-
ic array can be altered during execution. This is very flexible but may slow run-
time performance and lack any bounds checking during compilation. In
Fortran90 such arrays are called allocatable arrays.
The reasons for using an array are:
• Easier to declare (one variable name instead of tens or even thousands).
• Easier to operate upon (because of whole array operations the code is closer to
underlying mathematical form).
• Flexible accessing (it is easy operate on various sections (or parts) of an array in
different ways).
• Easier to understand the code (notational convenience).
• Inherent data parallelism (perform a similar computation on many data objects
simultaneously - if the program is running on a parallel machine).
• Optimization opportunities (for compiler designers).
Below is an example of an array containing eight integer elements:
5 7 13 24 0 65 5 22
Element
Each element has a subscript (or index) to identify its place in the array. Assuming that
the subscript starts at 1 then:
• the first element of the array is 5 with an index of 1
• the second element of the array is 7 with an index of 2
• the last element of the array is 22 with an index of 8
Subscript 1 Subscript 8
5 7 13 24 0 65 5 22
The upper array is a vector since it is one-dimensional while the lower array is a
matrix since it is two-dimensional. At most an array may have seven dimensions.
The term bounds refers to the lowest and highest subscript in each dimension. The vec-
tor above has a lower bound of 1 and a higher bound of 8, whereas the matrix has
bounds of 1 and 2 in the first dimension and 1 and 4 in the second dimension.
The extent of an array refers to the number of elements in a dimension. Hence the
above vector has an extent of 8, whereas the above matrix has an extent of 2 and 4 in
each dimension.
The shape of an array is a vector (i.e. an array!) containing the extents of an array.
Hence the above vector has a shape of (8) whereas the matrix has a shape of (2,4).
The term size refers to the total number of elements of an array, which simply is the
product of extents. The size of an array may be zero (see later). Both the vector and
matrix above have a size of 8.
Arrays that have the same shape are said to conform. This is the condition for whole
array or array section operations. The above examples do not conform with one
another. All arrays conform with scalar values.
5.2 Specifications
Like other variables arrays are specified with a specific data type (INTEGER, REAL,
derived type, etc.). For static arrays, the rank (up to a maximum of 7) and the bounds
(upper and lower) in each dimension are declared. Declaring the lower bound is
optional. If the lower bound is not specified Fortran90 assumes that the index begins
with 1.
Alternate and equivalent forms used to declare an array are as follows:
where [, attribute] allows for the declaration of other type attributes, if required.
The following declarations are equivalent. Both declare an integer array a with 6 ele-
ments; a real array b with 10 elements and a 2-dimensional logical array yes_no.
INTEGER, DIMENSION(6) :: a
REAL, DIMENSION(0:9) :: b
LOGICAL, DIMENSION(2,2) :: yes_no
INTEGER :: a(6)
REAL :: b(0:9)
LOGICAL :: yes_no(2,2)
Use the DIMENSION attribute form when several arrays of the same bounds and type
need to be declared. Use second form when several arrays of different types and/or
bounds need to be declared. A third form is a mixture of the two above, as shown
below:
where a takes the ‘default’ bounds bound1, but b takes another explicitly defined
value bound2.
A mixture of the three forms are allowed in the same program. Some further examples
are shown below:
The first statement declares x and y to have 8 elements each and z to have 16 ele-
ments. The second statement declares two arrays of the same type but with different
bounds. The third statement declares a rank 3 array. The last statement declares an
array which has 25 elements, each element being a character string of length 10.
It is possible to include arrays as components of a derived data type and to declare
arrays of derived data types, for example:
TYPE(point)
REAL :: position(3)
TYPE(point)
TYPE(point) :: object(10)
The type point is comprised of 3 real numbers, while the array object consists of 10
items of data, each consisting of 3 real numbers.
REAL, DIMENSION(8) :: a
INTEGER, DIMENSION(5,4) :: b
...
a(5) !fifth element
b(4,2) !element at intersection of the 4th row and 2nd column.
Subscripts (e.g. (i,j) ) refers to the element at the intersection of row i and column
j, where i and j have integer values between the upper and lower bounds for their
respective dimensions.
a(5) b(4,2)
Using expressions (e.g. (2*k) ) refers to an element whose subscript is the result of
the multiplication. The result of an expression must be an integer within the declared
bounds. It is an error to refer to a subscript beyond (either above or below) the range
of an array’s bounds.
5.3.2 Sections
As well as identifying individual elements it is possible to reference several elements
(called a section) with the same statement. Accessing a section of an array requires the
upper and lower bounds of the section to be specified together with a stride (for each
dimension). This notation is called a subscript triplet:
where lower and upper default to the declared extents if missing, and stride
defaults to 1.
REAL, DIMENSION(8) :: a
INTEGER, DIMENSION(5,4) :: b
INTEGER :: i=3
...
a(3:5) !elements 3, 4, 5
a(1:5:2) !elements 1, 3, 5
b(1:2,2:i) !elements (1,2) (2,2) (1,3) and (2,3)
b(i,1:4:2) !elements 1 and 3 of the third row
b(2:4,1) !elements 2, 3 and 4 of the first column
The bounds in a subscript triplet can take any (integer) values. Using Subscript tri-
plets is a compact and convenient way of referencing arbitrary sections of an array.
a(3:5) a(1:5:2)
Some more complex array section are given below. Note how the upper and lower
subscripts may be omitted:
REAL, DIMENSION(0:5) :: c
INTEGER, DIMENSION(4:5) :: d
c(:)
d(:,4)
c(:3)
d(::2,:)
c(::2)
(/ list /)
where list is a list of subscripts, in any order, to be referenced. Consider the follow-
ing example:
REAL, DIMENSION(9) :: a
INTEGER, DIMENSION(3) :: random
random=(/6,3,8/) !set values for random
a(random)=0.0 !a(6)=a(3)=a(8)=0.0
a((/7,8,9/))=1.2 !a(7)=a(8)=a(9)=1.2
For the vector subscript to be valid, list may not contain a value outside the bounds of
an array in which it is used to identify elements
Care must be taken not to duplicate values in a vector subscript when used in the LHS
of an expression. This would result in several values being written to the same array
element:
REAL, DIMENSION(5) :: a
INTEGER, DIMENSION(3) :: list
list=(/2,3,2/)
a(list)=(/1.1, 1.2, 1.3/) !illegal - element 2 set twice
a((/0,2,4/)) = 0.0 !illegal - subscript out of bounds
REAL, DIMENSION(3, 5) :: a
REAL, DIMENSION(100) :: a, b, c
REAL : d(10,10) = 0.0
b = 2*a+4
a = 2.0
c = b*a
c = d !illegal - arrays do not conform
The first assignment involves an array expression on the right hand side. Since a and
b conform it is a valid statement. Each element of b takes the corresponding value of a
multiplied by 2, plus 4.
The second assignment involves a scalar on the right hand side, (recall that scalars
conform with arrays of all shapes. Each element of a takes the value of 2.
The third assignment involves an array product on the right hand side. Since a and b
conform then their product can be evaluated, the product is an element by element
multiplication (not a matrix multiplication!). The result is another array which con-
forms with c, therefore each element of c is the product of the corresponding ele-
ments in a and b.
The last three statements all have conforming array sections of various sizes (5, 9 an 10
element respectively). The first of these sets every other element of beta to the first five
elements of alpha (beta(1)=alpha(1), beta(3)=alpha(2), etc.). The second
shows a powerful operation using arrays where values are shifted automatically and
without the need of DO loops. Elements 2,3,...10 of alpha take the value of elements
1,2,...9, thereby shifting values along in the array. The last assignment demonstrates
another important concept. Whereas the arrays beta and gamma do not conform, the
section of gamma used in the expression does conform with beta, hence the expres-
sion is a valid statement.
5.6.3 Renumbering
It is important to remember that the elements in an array section always have a lower
bound of 1 in each dimension. Regardless of the subscript of the element(s) in the
original array, elements of a section are renumbered so that indices are consecutive (in
each dimension) beginning at 1. Renumbered is automatic.
INTEGER :: nums(10), i
nums = (/ 1,3,5,7,9,2,4,6,8,0 /)
...
i = MAXLOC( nums ) !i=5, element 9 is maximum
i = MAXLOC( nums(1:5) ) !i=5, last element in section =9
i = MAXLOC( nums(3:8) ) !i=3, third element in section =9
i = MAXLOC( nums(::2) ) !i=3, third element in section =9
In the above example, the element with the value 9 is always the maximum element in
the array or section. However its index changes due to a renumbering of the section
The function SQRT() returns the square root of its argument. If the argument is a sca-
lar variable, a single value is returned. If the argument is an array, an array of square
roots is returned. Hence, every element of a is substituted by the square root of the
existing value.
The third assignment finds the string length for each element of c and rejects any trail-
ing blanks. Hence, if c(1) is ‘Alexis ‘ the TRIM() function returns ‘Alexis ‘ (i.e.
minus the trailing blank), the length of which is 6.
Many of the intrinsic functions (including the mathematical functions) may take
arrays, array sections or simple scalar variables as arguments.
handling of certain situations without the need of extra code. As an example consider
the following situation:
INTEGER :: a(5)=(/1,2,1,1,3/)
...
a(1:COUNT(a==1))=0 !a=(/ 0,0,0,1,3 /)
a(1:COUNT(a==4))=1 !a unchanged
The first statement initialises a using a constructor (see below). The function
COUNT() returns the number of elements which have a value equal to 1 and 4 respec-
tively. The first statement sets a(1:3) to zero, but the second does nothing (because
no element of a has the value 4, and the array section a(1:0) is of zero-size).
Allowing for zero-sized arrays means that if an array or section has no elements the
statement becomes a ‘do nothing’ statement and the programmer is saved from hav-
ing to deal with the problem.
TYPE(circle)
INTEGER :: radius
REAL :: area
END TYPE circle
Previously, a second derived type called position was used to store the real coordi-
nates of the circles centre; position had two real numbers as components. It is possi-
ble to replace the use of the derived type position by a real array:
TYPE(circle)
REAL, DIMENSION(2) :: pos
INTEGER :: radius
REAL :: area
END TYPE circle
Here pos(1) holds say an x coordinate while pos(2) holds a y coordinate. Arrays
can be used when the number of components becomes to large (or just inconvenient).
Array components are referenced in much the same way as other components:
TYPE(circle) :: first
...
first%pos(1) !element 1 of pos
first%pos(1:) !whole array (section)
Just as several (or many) instances of an intrinsic data type may be grouped together
as a single array, so it is possible to group together instances of derived data types. For
example:
An array of a derived data type and/or an array component of a derived type have
the same requirements (i.e. conformance in expressions, etc.) and restrictions as other
arrays in Fortran 90. For example:
Care must be taken to ensure that any labelling of array sections is applied to the cor-
rect part of an expression.
array = (/ list /)
INTEGER :: a(6)=(/1,2,3,6,7,8/)
• variable expression(s)
REAL :: b(2)=(/SIN(x),COS(x)/)
• array expression(s)
INTEGER :: c(5)=(/0,a(1:3),4/)
• implied DO loops (see later)
REAL :: d(100)=(/REAL(i),i=1,100/)
The constructor can be used during declaration as shown above or in a separate state-
ment. Arrays with a rank of two or more should not be initialise with a simple con-
structor, but instead should use a combination of constructor(s) and the RESHAPE()
function (see below).
5.9.2 Reshape
The RESHAPE() function is to be used for the initialisation or assignment of multi-
dimensional arrays, i.e., arrays with rank greater than 1. It can be used on a declara-
tion statement or in a separate statement. The format is:
where list is a 1-dimensional array or constructor containing the data, and shape a
1-dimensional array or vector subscript containing the new shape of the data. PAD is
an array containing data to be used to pad out the data in list to the required shape.
ORDER can be used to change the order in which data is reshaped.
The size of the array determines the dimension of the new array. The elements deter-
mine the extent of each dimension. Consider the following example:
INTEGER, DIMENSION(3,2) :: a
a=RESHAPE((/0,1,2,3,4,5/),(/3,2/)) !put values into a
RESHAPE() will generate a rank 2 array with extents 3 and 2 from the list of values in
the constructor. Since this array conforms with the array a, whole array assignment is
used to give each element of a the required value. Unless the ORDER argument is used
values from the constructor will be returned in array element order, i.e. a(1,1)=0,
a(2,1)=1, a(3,1)=2, a(1,2)=3, etc...
The first DATA statement uses a list by value where the value for each array element is
explicitly declared. The second DATA statement uses a list by whole array where 4 is
the size of the array and 0 is the required value. Do not confuse with the multiplica-
tion operator. The third and fourth statements use a list by section where the first row
takes values 0 and 0 and the second row takes the values of 1 and 1.
The last two statements use a list by implied DO loops (see later) where the odd
indexed elements are assigned the value 1 and the even indexed elements take the
value of 2.
Remember that:
• a DATA statement can split in more than one line but each line must have a DATA
keyword.
• may be used for other variables as well as arrays.
5.10 WHERE
A WHERE statement is used to control which elements of an array are used in an
expression, depending on the outcome of some logical condition. It takes a statement
or a construct form. The WHERE statement allows the expression on an element by ele-
ment basis only if a logical condition is true. The syntax is as follows:
INTEGER :: a(2,3,4)
WHERE( a<0 ) a = 0
WHERE( a**2>10 ) a = 999
WHERE( a/=0 ) a = 1/a
The first WHERE statement results in all negative values of a being set to zero, the non-
negative values remain intact. The second WHERE statement results in elements of
data being set to 999 if their square is greater than ten. The third statement calculates
the reciprocal of each element of data, except those with a value of zero (hence avoid-
ing the error ‘divide by zero’).
The WHERE construct allows array assignment(s) (again on an element by element
basis) only if a logical condition is true, but may provide alternative array assign-
ment(s) if false. The syntax is as follows:
WHERE (condition)
block1
[ELSEWHERE
block2]
ENDWHERE
INTEGER :: btest(8,8)
...
WHERE ( btest<=0 )
btest = 0
ELSEWHERE
btest = 1/btest
ENDWHERE
All negative valued elements of btest are set to zero and the rest take their reciprocal
value. A WHERE statement or construct is one way of avoiding ‘divide by zero’ errors
at run-time.
ALL( condition )
and determines whether all elements in an array satisfy the condition. The result is the
logical value .TRUE. if all elements satisfy the condition and .FALSE. otherwise.
The first statement returns .false. since the first element is equal to 5 and not
greater. The second statement returns .true. since all elements have a value less
than 20. The third statement returns .true.since all element have a value 5 or
greater and the value of test2 is .true..
and returns the extent of an array for the specified dimension (specified by the argu-
ment DIM). If the dimension is missing SIZE() returns the total number of elements
in the array.
REAL, DIMENSION(3,2) :: a
num=Size(a) !num=6
num=Size(a,DIM=1) !num=3
num=Size(a,DIM=2) !num=2
DIM=2 DIM=1
The value given to the DIM argument specifies the dimension, DIM=1 returns the
number of rows, DIM=2 the number of columns, etc.
and replicates the given array by adding a dimension, where DIM stands for dimen-
sion and NCOPIES for number of copies.
The first SPREAD statement replicates array a three times on the row dimension. The
second SPREAD statement replicates array a three times on the column dimension.
b 2 3 4 c 2 2 2
2 3 4 3 3 3
2 3 4 4 4 4
MAXLOC(array, [mask])
determines the location of the element which has the largest value in an array and sat-
isfies the optional mask. A mask is a logical array (or condition involving an array)
which conforms to array. The only elements of array which take part in the search
for the maximum valued elements are those which correspond to .TRUE. elements in
the mask.
REAL :: a(5)=(/2,8,5,3,4/)
num = MAXLOC( a ) !num=2
num = MAXLOC( a(2:4) ) !num=1, note renumbering
num = MAXLOC( a, MASK=a<5 ) !num=5
a MASK=a<5
MAXLOC( 2 8 5 3 4 T F F T T )
MAXLOC( 2 3 4 )
The first statement returns 2 since this is the position of the highest number on the list.
The second MAXLOC() statement returns the value 1 since this is the position of the
highest valued element in the array section. Remembering that elements in array sec-
tion statements are renumbered with one as the lower bound in each dimension. The
third MAXLOC() statement returns 5 since this is the position of the highest number
on the list when numbers greater than 5 are excluded by the mask.
5.12 Exercises
1. Consider the following array:
INTEGER, DIMENSION(3,3) :: a
a = RESHAPE( (/ 1, 2, 5, 8, 6, 7, 5, 0, 0 /), (/3,3/) )
What is the value of element a(2,1); a(3,2); a(1,2); a(2,3). Write a program to dis-
play all required values.
2. Given the following declarations:
REAL, DIMENSION(1:10,1:20) :: a
REAL, DIMENSION(10,-5:10) :: b
REAL, DIMENSION(0:5,1:3,6:9) :: c
REAL, DIMENSION(1:10,2:15) :: d
What is the rank, size, bounds, and extents of a, b, c and d? Write a program
which uses the intrinsic functions SIZE(), LBOUND(), UBOUND() and
SHAPE() to display the required information.
3. Declare an array for representing a chessboard (a board of 8x8), indicating a
white square with .false., and a black square with .true..
4. Given the following declarations:
REAL :: a(0:5,3)=1.0
5 6 1
4 2 2
0 5 3
8. Using vector subscripts declare a rank 1 array zeta with 30 elements and place
the following values in the array:
a) 1.0 to the 1st and 2nd elements.
b) 2.0 to the 10th, 12th, 14th and 16th elements.
c) 3.0 to 24th, 25th, 27th and 22th element.
9. For the following array declarations, which of the following statements are
valid (i.e. for which of the following are the array expressions conforming?)
Test your answer by writing a program.
alpha = (/-1,(0,i=2,49),1/)
alpha = (/-1,(0,i=1,48),1/)
alpha = (/-1,(0,i=37,84),1/)
alpha = (/-1,48*0,1/)
12. If alpha has been declared and initialised as follows
Write a program which sets up a rank one array to hold the values 1,2,3,...,10.
Using the intrinsic function PRODUCT() (which returns the product of all array
elements passed to it) and various array sections, calculate:
a) The number of permutations n=5 people may pose for a photo standing in
r=1 rows.
b) the number of permutations n=8 students may sit in a row of r=4 front row
desks
6 Control statements
Only if expression (a logical variable or expression) has the value .TRUE. is state-
ment executed. For example:
Here, if x is less than zero then it is given a new value, otherwise x retains it’s previ-
ous value. The IF statement is analogous to phrases like ‘if it is raining, take an
umbrella’.
The structure of an IF construct depends on the number of conditions to be checked,
and has the following general form:
block]
ENDIF [name]
LOGICAL :: rain
INTEGER :: numb=0, ncoat
...
IF ( rain ) THEN
ncoat = 1
numb = numb+1
ENDIF
If rain is .TRUE. the block of statements are executed and control passes to the next
statement after ENDIF, otherwise the block of statements is skipped and control
passes directly to the next statement after ENDIF.
More complex situation can occur when performing alternative actions depending on
a single condition. For instance, the previous examples does not make a distinction
between levels of rainfall. The example above can be rephrased as ‘if there is light rain
then take a coat otherwise (else) take a coat and an umbrella’.
REAL :: inches_of_rain
INTEGER :: numb=0, ncoat
...
IF( inches_of_rain>0.05 ) THEN !heavy rain
ncoat = 1
numb = numb+1
ELSE !light rain
ncoat = 1
ENDIF
Notice the use of the ELSE part separating different options and that each block may
contain one or more statements. The second block of statements acts as a set of
‘default’ statements for when the condition is not satisfied. The passing of control fol-
lows the same rules as mentioned above.
There are situations when alternative actions depend on several conditions. For exam-
ple, a discount applied to a purchase may vary depending on the number of items
purchased, the larger the purchase the larger the discount; i.e.
Notice the use of the ELSEIF to add further conditions to the block (other discount
bands in this case). The ELSE statement acts as a default in order to cover other even-
tualities. Again, the same rules concerning passing of control apply.
IF constructs can be labelled. Naming constructs can be useful when one is nested
inside another, this kind of labelling makes a program easier to understand, for exam-
ple:
INTEGER :: month
The above example prints a season associated with a given month. If the value of the
integer month is not in the range 1-12 the default case applies and the error message
‘not a month’ is printed, otherwise one of the CASE statements applies. Notice that
there is no preferred order of values in a CASE statement.
6.1.3 GOTO
The GOTO statement can be used to transfer control to another statement, it has the
form:
GOTO label
The GOTO statement simply transfers control to the statement, skipping any state-
ments in between. For example:
...
IF( x<10 ) GOTO 10
...
10 STOP
The GOTO statement should be avoided where ever possible, programs containing
such statements are notoriously hard to follow and maintain. The STOP statement ter-
minates a program.
6.2 Repetition
An important feature of any programming language is the ability to repeat a block of
statements. For example, converting a character from upper to lower case (or visa
versa) can be done in a single executable statement. In order to convert several charac-
ters (in say a word or sentence) one has to either repeat the statement or re-execute the
program. Using a loop construct it is possible to restructure the program to repeat the
same statement as many times as required.
6.2.1 DO construct
In Fortran 90 it is the DO loop (or construct) which enables the programmer to repeat a
a block of statements. The DO construct has the general form:
value stop (or an integer value no greater than stop). The number of times the state-
ments will be executed can be calculated from:
It is possible for stop to be less than start and for step to be negative. In this case
the loop counts backwards (note this is in contrast to array sections which have zero
size if the upper bound is ever below the lower bound!) If stop is smaller than start
and step is positive then count will take the value zero and the statement(s) will not
be executed at all. The value of count is not allowed to change within the loop. For
example:
INTEGER :: i, j, k
all: DO i=1,10
WRITE(*,*) i !write numbers 1 to 10
ENDDO all
nil: DO j=10,1
WRITE(*,*) j !write nothing
ENDDO nil
even: DO k=10,2,-2
WRITE(*,*) k !write even numbers 10,8,6,4,2
END DO even
[name:] DO
block
ENDDO [name]
The block of statements will be repeated forever, or at least until somebody stops the
program. In order to terminate this type of loop the programmer must explicitly
transfer control to a statement outside the loop.
The CYCLE statement transfers control back to the beginning of the loop to allow the
next iteration of the loop to begin (thereby skipping the rest of the current iteration).
For example:
INTEGER :: int
...
name: DO
READ(*,*) int !read in a number
IF (int<0) CYCLE name !if negative, read in another
...
ENDDO name
The name of the loop can be omitted from an EXIT or CYCLE statement. However
confusion can arise from multiple and nested (i.e. one inside another) DO loops, EXIT
and CYCLE statements, therefore naming loops is highly recommended.
Where loops are nested, unnamed EXIT and CYCLE statements refer to the inner most
loop in which they sit. It is possible to pass control from any inner loop to any outer
loop by specifying the loop name. As an example consider the following:
outer: DO i=1,10
inner1: DO
IF( x<0 ) EXIT !exit loop inner1
IF( x==0 ) EXIT outer !exit loop outer
...
ENDDO inner1
inner2: DO
IF( x<0 ) CYCLE !cycle loop inner2
IF( x==0 ) CYCLE inner1 !illegal
...
ENDDO inner2
...
ENDDO outer
6.3 Nesting
Placing one block construct inside another (IF-THEN statements inside DO loops, etc.)
is possible, but the inner block(s) must by completely within the outer block(s). It is
illegal to overlap nested statements. For example:
This is generally true of all constructs, i.e. DO loops, IF-THEN-ELSEIF and CASE con-
structs. The maximum level of nesting (constructs inside constructs inside con-
structs...) is compiler dependant. In the case of the NAG compiler this level is 20 for
DO loops and 30 for CASE constructs.
6.4 Exercises
1. Write a program which reads in a single character and returns a character
according to the following conditions:
- if an upper case letter is entered it returns the lower case equivalent.
- if a lower case letter is entered it returns the upper case equivalent.
- if a non-letter is entered it does absolutely nothing.
Hint: In the ANSI collating sequence upper and lower case letters differ by 32;
So to convert from upper to lower use the character-to-integer (IACHAR() )
and integer-to-character (ACHAR()) ANSI based function as follows:
Write a program which reads the employee's number of hours worked. If the
hours worked exceed 60, print an appropriate error message. Otherwise print
the expected payment.
3. Given the power (watts) and the voltage (volts) the current (amps) drawn in a
circuit can be found from: Current=(Power)/(Voltage).
Write a program that calculates the current given the power of a device
(remember that the voltage is 240v in the UK) and displays the most suitable
cable for use. Consider 3 suitable cables (up to 5 amps, up to 13 amps, and up to
30 amps). In case a suitable cable can not be found the program should print an
appropriate message.
4. Predict the values loop takes and the value loop has after termination of each
of the following DO constructs. Your predictions may be tested by writing a pro-
gram which reads in the values used in the loop control clause (start, stop and
step) as input.
(a) DO loop=5, 3, 1
(b) DO loop=-6, 0
(c) DO loop=-6, 0, -1
(d) DO loop=-6, 0, 1
(e) DO loop=6, 0, 1
(f) DO loop=6, 0, -1
(g) DO loop=-10, -5, -3
(h) DO loop=-10, -5, 3
5. Write a program which prints a multiplication table (i.e. 1n=?, 2n=?,... 12n=?).
Allow the user to determine which table (value of n) they require.
6. Write a program to calculate and display the size of A0 to A6 papers in both
mm and inches. Use following formula:
((1 ⁄ 4) – (n ⁄ 2))
Height ( cm ) = 2
(– (1 ⁄ 4) – (n ⁄ 2))
Width ( cm ) = 2
Where n is the size of the paper 0 to 6, and one inch=2.54cm.
7. Write a program to produce the Fibonacci sequence. This sequence starts with
two integers, 1 and 1. The next number in the sequence is found by adding the
previous two numbers; for example, the 4th number in the series is the sum of
the 2nd and the 3rd and so on. Terminate when the nth value is greater than
100.
8. The increase in temperature dT of a chemical reaction can be calculated using:
dT = 1 – exp ( – kt )
k = exp ( – q )
q = 2000 ⁄ ( T + 273.16 )
where T is the temperature in centigrade, and t is the time in seconds. Write a
program which prints the temperature of such a reaction at 1 minute intervals,
The initial temperature is supplied by the user and the above equations should
be re-calculated once every second. The program should terminate when the
temperature reaches twice the initial temperature.
7 Program units
Main
program Module
External
procedure
Internal
procedures
Module
procedures
Dividing a program into units has several advantages:
• Program units can be written and tested independently.
• A program unit that has a well defined task is easier to understand and main-
tain.
• Once developed and tested modules and external procedures can be re-used in
PROGRAM [name]
[specification statements]
[executable statements]
...
[CONTAINS
internal procedures]
END [PROGRAM [name]]
The PROGRAM statement marks the beginning of the main program unit while the END
PROGRAM statement not only marks the end of the unit but also the end of the pro-
gram as a whole. The name of the program is optional but advisable. The CONTAINS
statement serves to identify any procedures that are internal to the main program
unit. (Internal procedures are dealt with later on in this chapter.) When all executable
statements are complete, control is passed over any internal procedures to the END
statement.
A program can be stopped at any point during its execution, and from any program
unit, through the STOP statement:
STOP [label]
where label is an optional character string (enclosed in quotes) which may be used
to inform the user why and at what point the program has stopped.
7.3 Procedures
Procedures are a type of program unit, and may be either subroutines or functions.
Procedures are used to group together statements that perform a self-contained, well
defined task. Both subroutines and functions have the following general form:
In both cases control is passed to the procedure from the referencing statement, and is
returned to the same statement when the procedure exits. The argument list are zero
or more variables or expressions, the values of which are used by the procedure.
REAL, DIMENSION(10) :: a, c
...
CALL swap( a,c )
REAL :: y,x,c
...
y = line( 3.4,x,c )
PROGRAM outer
REAL :: a, b, c
...
CALL inner( a )
...
CONTAINS
The program outer contains the internal subroutine inner. Note that variables
defined in the host unit remain defined in the internal procedure, unless explicitly
redefined there. In the example, although a, b and c are all defined in outer:
• The value of a is passed by argument to a redefined variable (dummy argument)
also called a. Even though they hold the same value, the variables a are different
objects.
• Like a, the variable b is redefined in the subroutine and so is a different object to
b in the host program. The value of b is not passed by argument or by host as-
sociation.
• c is a single object, common to both outer and inner through host association.
In order to prevent redefining a variable by mistake, it is good practice to declare all
variables used in a procedure.
PROGRAM first
REAL :: x
x = second()
...
END PROGRAM first
External procedures have no host program unit, and cannot therefore share data
through host association. Passing data by argument is the most common way of shar-
ing data with an external procedure. External procedures may be referenced by all
other types of procedure.
7.4.1 SAVE
The SAVE attribute forces the program to retain the value of a procedure variable from
one call to the next. Any variable that is given an initial value in its declaration state-
ment has the SAVE attribute by default. For example:
The first time the function func1 is referenced, a_old has an undefined value while
counter is set to zero. These values are reset by the function and saved so that in any
subsequent calls a_old has the value of the previous argument and counter is the
number of times func1 has previously been referenced.
Note: it is not possible to save dummy arguments or function results!
Where ever possible interfaces should be made explicit. This can be done through the
interface block:
INTERFACE
interface statements
END INTERFACE
The interface block for a procedure is included at the start of the referencing program
unit. The interface statements consist of a copy of the SUBROUTINE (or FUNCTION)
statement, all declaration statements for dummy arguments and the END SUNROU-
TINE (or FUNCTION) statement. For example:
PROGRAM count
INTERFACE
SUBROUTINE ties(score, nties)
REAL :: score(50)
INTEGER :: nties
END SUBROUTINE ties
END INTERFACE
REAL, DIMENSION(50):: data
...
CALL ties(data, n)
...
END PROGRAM count
The interface block in the program count provides an explicit interface to the subrou-
tine ties. If the count were to reference other external procedures, their interface
statements could be placed in the same interface block.
The dummy arguments data1 and data3 are both arrays which have been declared
with a rank but no size, the colon ‘:’ is used instead of a specific size in each dimen-
sion. Similarly str has no explicit length, it adopts the length of the actual argument
string. When the subroutine sub2 is called, all three dummy arguments assume the
size of their corresponding actual arguments; all three dummy arguments are
assumed shape objects.
The subroutine invert has three dummy arguments. a is used in the procedure but is
not updated by it and therefore has INTENT(IN). inverse is calculated in the sub-
routine and so has INTENT(OUT). count (the number of times the subroutine has
been called) is incremented by the procedure and so requires the INTENT(INOUT)
attribute.
INTEGER :: x=0
...
CALL sub2( a=1, b=2, stat=x )
CALL sub2( 1, stat=x, b=2)
CALL sub2( 1, 2, stat=x )
The dummy variable names act as keywords in the call statement. Using keywords,
the order of arguments in a call statement can be altered, however keywords must
come after all arguments associated by position:
When using keyword arguments the interface between referencing program unit and
procedure must be explicit. Note also that arguments with the INOUT attribute must
be assigned a variable and not just a value, stat=0 would be illegal.
SUBROUTINE sub1(a, b, c, d)
INTEGER, INTENT(INOUT):: a, b
REAL, INTENT(IN), OPTIONAL :: c, d
...
END SUBROUTINE sub1
Here a and b are always required when calling sub1. The arguments c and d are
optional and so sub1 may be referenced by:
CALL sub1( a, b )
CALL sub1( a, b, c, d )
CALL sub1( a, b, c )
Note that the order in which arguments appear is important (unless keyword argu-
ments are used) so that it is not possible to call sub1 with argument d but no argu-
ment c. For example:
Optional arguments must come after all arguments associated by position in a refer-
encing statement and require an explicit interface.
It is possible to test whether or not an optional argument is present when a procedure
is referenced using the logical intrinsic function PRESENT. For example:
REAL :: inverse_c
If the optional argument is present then PRESENT returns a value .TRUE. In the above
example this is used to prevent a run-time error (dividing by zero will cause a pro-
gram to ‘crash’).
PROGRAM test
INTERFACE
REAL FUNCTION func( x )
REAL, INTENT(IN) ::x
END FUNCTION func
END INTERFACE
...
CALL sub1( a, b, func(2) )
...
END PROGRAM test
When the call to sub1 is made the three arguments will be a, b and the result of func,
in this case the return value is 1/2. The procedure that is used as an argument will
always execute before the procedure in whose referencing statement it appears
begins. Using a procedure as an argument requires an explicit interface.
Note that the specification statement for the function func identifies the result as being
of type REAL, this is an alternative to declaring the function name as a variable, i.e.
and
FUNCTION func( x )
REAL :: func
REAL, INTENT(IN) :: x
func = 1/x
END FUNCTION func
are equivalent.
7.7 Recursion
It is possible for a procedure to reference itself. Such procedures are called recursive
procedures and must be defined as such using the RECURSIVE attribute. Also for
functions the function name is not available for use as a variable, so a RESULT clause
must be used to specify the name of the variable holding the function result, for exam-
ple:
an array of integers. The difference between the two arrays is likely to be the data type
of the dummy arguments.
For convenience, Fortran 90 allows two or more procedures to be referenced by the
same, generic name. Exactly which procedure is invoked will depend on the data type
(or rank) of the actual argument(s) in the referencing statement. This is illustrated by
some of the intrinsic functions, for example:
The SQRT() intrinsic function (returns the square root of its argument) can be given a
real, double precision or complex number as an argument:
• if the actual argument is a real number, a function called SQRT is invoked.
• if the actual argument is a double precision number, a function called DSQRT is
invoked.
• if the actual argument is a complex number, a function called CSQRT is invoked.
A generic interface is required in order to declared a common name and to identify
which procedures can be referred to by the name. For example:
INTERFACE swap
SUBROUTINE iswap( a, b )
INTEGER, INTENT(INOUT) :: a, b
END SUBROUTINE iswap
SUBROUTINE rswap( a, b )
REAL, INTENT(INOUT) :: a, b
END SUBROUTINE rswap
END INTERFACE
The interface specifies two subroutines iswap and rswap which can be called using
the generic name swap. If the arguments to swap are both real numbers then rswap is
invoked, if the arguments are both integers iswap is invoked.
While a generic interface can group together any procedures performing any task(s) it
is good programming practice to only group together procedures that perform the
same operation on a different arguments.
7.9 Modules
Modules are a type of program unit new to the Fortran standard. They are designed to
hold definitions, data and procedures which are to be made available to other pro-
gram units. A program may use any number of modules, with the restriction that each
must be named separately.
The general form of a module follows that of other program units:
MODULE name
[definitions]
...
[CONTAINS
module procedures]
END [MODULE [name]]
In order to make use of any definitions, data or procedures found in a module, a pro-
gram unit must contain the statement:
USE name
at its start.
MODULE global
REAL, DIMENSION(100) :: a, b, c
INTEGER :: list(100)
LOGICAL :: test
END MODULE global
All variables in the module global can be accessed by a program unit through the
statement:
USE global
The USE statement must appear at the start of a program unit, immediately after the
PROGRAM or other program unit statement. Any number of modules may be used by a
program unit, and modules may even use other modules. However modules cannot
USE themselves either directly (module A uses A) or indirectly (module A uses mod-
ule B which uses module A).
It is possible to limit the variables a program unit may access. This can act as a ‘safety
feature’, ensuring a program unit does not accidentally change the value of a variable
in a module. To limit the variables a program unit may reference requires the ONLY
qualifier, for example:
This ensures that a program unit can only reference the variables a and c from the
module global. It is good programming practice to USE ... ONLY those variables
which a program unit requires.
A potential problem with using global variables are name clashes, i.e. the same name
being used for different variables in different parts of the program. The USE statement
can overcome this by allowing a global variable to be referenced by a local name, for
example:
Here the variable state is the local name for the variable test. The => symbol asso-
ciates a different name with the global variable.
ment. A module procedure may call other module procedures within the same
module or in other modules (through a USE statement). A module procedure also has
access to the variables declared in a module through ‘host association’. Note that just
as with other program units, variables declared within a module procedure are local
to that procedure and cannot be directly referenced elsewhere.
One of the main uses for a module is to group together data and any associated proce-
dures. This is particularly useful when derived data types and associated procedures
are involved. For example:
MODULE cartesian
TYPE point
REAL :: x, y
END TYPE point
CONTAINS
SUBROUTINE swap( p1, p2 )
TYPE(point), INTENT(INOUT):: p1
TYPE(point), INTENT(INOUT):: p2
TYPE(point) :: tmp
tmp = p1
p1 = p2
p2 = tmp
END SUBROUTINE swap
END MODULE cartesian
The module carteasian contains a declaration for a data type called point. car-
tesian also contains a module subroutine which swaps the values of its point data
type arguments. Any other program unit could declare variables of type point and
use the subroutine swap via the USE statement, for example:
PROGRAM graph
USE cartesian
TYPE(point) :: first, last
...
CALL swap( first, last)
...
END PROGRAM graph
MODULE one
PRIVATE !set the default for module
REAL, PUBLIC :: a
REAL :: b
PUBLIC :: init_a
CONTAINS
SUBROUTINE init_a() !public
...
INTERFACE generic_name
MODULE PROCEDURE name_list
END INTERFACE
where name_list are the procedures to be referenced via generic_name, for exam-
ple a module containing generic subroutines to swap the values of two arrays includ-
ing arrays of derived data types would look like:
MODULE cartesian
TYPE point
REAL :: x, y
END TYPE point
INTERFACE swap
MODULE PROCEDURE pointswap, iswap, rswap
END INTERFACE
CONTAINS
SUBROUTINE pointswap( a, b )
TYPE(point) :: a, b
...
END SUBROUTINE pointswap
MODULE strings
INTERFACE OPERATOR ( / )
MODULE PROCEDURE num
END INTERFACE
CONTAINS
Usually, the / operator is not defined for characters or strings but the module strings
contains an interface and defining function to allow a string to be divide by a charac-
ter. The result of the operation is the number of times the character appears in the
string:
USE strings
...
i = ‘hello world’/’l’ !i=3
i = ‘hello world’/’o’ !i=2
i = ‘hello world’/’z’ !i=0
MODULE cartesian
TYPE point
REAL :: x, y
END TYPE point
INTEFACE OPERATOR ( .DIST. )
MODULE PROCEDURE dist
END INTERFACE
CONTAINS
REAL FUNCTION dist( a, b )
TYPE(point) INTENT(IN) :: a, b
dist = SQRT( (a%x-b%x)**2 + (a%y-b%y)**2 )
END FUNCTION dist
END MODULE cartesian
The operator .DIST. is used to find the distance between two points. The operator is
only defined for the data type point, using it on any other data type is illegal. Just as
with overloaded operators, the interface and defining function are held in a module. It
makes sense to keep the derived data type and associated operator(s) together.
Any program unit may make use of the data type point and the operator .DIST. by
using the module cartesian, for example:
USE cartesian
TYPE(point) :: a, b
REAL :: distance
...
distance = a .DIST. b
tine must have two, non-optional arguments, the first must have INTENT(INOUT) or
INTENT(OUT); the second must have INTENT(IN). For example:
MODULE cartesian
TYPE point
REAL :: x, y
END TYPE point
INTEFACE ASSIGNMENT( = )
MODULE PROCEDURE max_point
END INTERFACE
CONTAINS
SUBROUTINE max_point( a, pt )
REAL, INTENT(OUT) :: a
TYPE(point), INTENT(IN) :: pt
a = MAX( pt%x, pt%y )
END SUBROUTINE max_point
END MODULE cartesian
Using the module cartesian allows a program unit to assign a type point to a type
real. The real variable will have the largest value of the components of the point varia-
ble. For example:
USE cartesian
TYPE(point) :: a = point(1.7, 4.2)
REAL :: coord
...
coord = a !coord = 4.2
7.13 Scope
7.13.1 Scoping units
The scope of a named entity (variable or procedure) is that part of a program within
which the name or label is unique. A scoping unit is one of the following:
• A derived data type definition.
• An interface block, excluding any derived data type definitions and interface
blocks within it.
• A program unit or internal procedure, excluding any derived data type defini-
tions and interfaces.
All variables, data types, labels, procedure names, etc. within the same scoping unit
must have a different names. Entities with the same name, which are in different scop-
ing units, are always separate from one another.
The scope of a name declared in a module extends to all program units that use that
module, except where an internal procedure re-declares the name.
The names of program units are global and must therefore be unique. The name of a
program unit must also be different from all entities local to that unit. The name of an
internal procedure extends throughout the containing program unit. Therefore all
internal procedures within the same program unit must have different names.
The following shows an example of scoping units:
7.14 Exercises
1. Write a program with a single function to convert temperatures from Fahren-
heit to Centigrade. In the body of the main program read in the temperature to
be converted, and output the result. The actual calculation is to be done in a
function.
a) Write an internal function which requires no actual arguments, but which
uses host association to access the value to be converted. The result of the func-
tion is the converted temperature.
b) Write an external function which requires the temperature to be converted to
be passed as a single argument. Again the function result is the converted tem-
perature. Do not forget to include an interface block in the main program.
Use the following formula to convert from Fahrenheit to Centigrade:
Centigrade = ( Fahrenheit – 32 ) × ( 5 ⁄ 9 )
2. Write a program with a single subroutine to sort a list of integer numbers into
order. In the main program read a list of random integers (about 5) into an
array, call the subroutine to perform the sort, and output the array.
a) Write an internal subroutine which requires no actual arguments, but which
uses host association to access the array to be sorted.
b) Write an external subroutine which requires that the array to be sorted be
passed as an argument. The external subroutine will require an interface block.
Use the following selection sort algorithm to sort the values in an array a:
last = SIZE( a )
DO j=1, last-1
swap_index = MINLOC( a(j:last) )
tmp = a( j )
a( j ) = a( (j-1)+swap_index(1) )
a( (j-1)+swap_index(1) ) = tmp
END DO
The selection sort algorithm passes once through the array to be sorted, stop-
ping at each element in turn. At each element the remainder of the array is
checked to find the element with the minimum value, this is then swapped
with the current array element.
3. Write a program which declares three rank one, real arrays each with 5 ele-
ments and that uses array constructors to set a random value for each element
(say between 1 and 20) for each array. Write an internal subroutine which finds
the maximum value in an array (use the MAX and MAXVAL intrinsic function)
and reports and SAVEs that value. Call the subroutine once for each array, the
final call should report the maximum value from all arrays.
4. Change the subroutine in written in 3 to accept arrays of any size (if you have
not already done so). Test the new subroutine by calling it with three arrays,
each of different size.
5. Write a program which declares an rank 1, integer array and use a constructor
to set values for each element in the range -10 to 10. The program will pass the
array as an argument to an external subroutine, along with two optional argu-
ments top and tail.
The subroutine is to replace any values in the array greater than top with the
value of top; similarly the subroutine replaces any values lower than tail
with tail. The values of top and tail are read in by the main program. If
either top or tail is absent on call then no respective action using the value is
taken. (Remember it is good programming practice to refer to all optional argu-
ments by keyword.)
6. Write a module to contain the definition for a derived data type point, which
consists of two real numbers representing the x an y coordinates of that point.
Along with this declaration, include a global parameter representing the origin
at (0.0,0.0).
The module should also contain a function to calculate the distance between
two arbitrary points (this is done earlier in the notes, as an operator). Write a
program to read in an x and y coordinate and calculate its distance from the ori-
gin.
7. Using the selection sort algorithm in question 2 write a module containing two
subroutines, one which sorts real arrays the other which sorts integer arrays
(both of rank one). The module should provide a generic interface to both sub-
routines. Check the module and the generic interface by writing a program that
uses the module.
This chapter deals with the interaction between a user and the program via the stand-
ard input and output devices, namely the keyboard and screen. Data can be stored
and represented in several different ways; programs store data in binary form (called
unformatted data) while programmers and program users perfer to work with charac-
ters (or formatted data). Almost all interactive input and output (I/O) uses characters
and hence formatted data.
When data is read into a program, the characters are converted to the machine’s
binary form. Similarly, data stored in a binary form is converted when written to the
screen. The layout or formatting of data can be specified by a programmer or take the
default format used in Fortran 90. A subset of the formatting facilities is presented
later, the full set is rarely used.
The process of I/O can be summarised as:
Binary Computer
representation
Screen Keyboard
BE1D7DBF
which is difficult to understand (and hence of limited use when written to screen) but
corresponds to the real value 0.00045. This may be formatted and written as any or all
of:
0.45E-03
4.5E-04
0.000450
READ(*,*) radius
and the value of the variable area would be displayed on the screen by:
WRITE(*,*) area
unit is an integer associated with the screen or a file (see later) and format describes
how the data should look. When reading from the keyboard unit can be either 5 or *;
when writing to the screen unit can be either 6 or *.
Several variables (or expressions) may be specified on one READ or WRITE statement.
INTEGER :: i, j
REAL :: data(3)
...
READ(*,*) i
WRITE(*,*) i, j, data
The *s allow a program to use I/O defaults. The first * represents a location (e.g. ‘read
from the standard input’) while the second * represents the default format of the vari-
ables which changes from data type to data type. This form of outputting data is
quick, simple and convinient.
Write to screen
WRITE (*,*)
Use default format
READ(*,100) i, j
WRITE(*,100) i, j
READ(*,FMT=200) x, y
WRITE(*,200) x, y
...
100 FORMAT (2I8) !2I8 is an edit descriptor
200 FORMAT (2F10.6) !2F10.6 is an edit descriptor
Formatting is sometimes known as I/O editing. The I/O is controlled using edit
descriptors (explianed later). The general form of a FORMAT statement is:
where label is an identifying number (unique to that part of the program) and
flist is a list of edit descriptors which include one or more of:
since many of the edit descriptors cover advanced features, such as output of binary
number, etc. and are of limited general use.
The labelled FORMAT statement may be replaced by specifying the format descriptor
list as a character string directly in the WRITE or READ statement, as follows:
INTEGER :: i, j
REAL :: x, y, z
...
READ (*,’(2I8)’) i, j
WRITE (*,’(3F12.6)’) x, y, z
This has the advantage of improved clarity, i.e. the reader does not have to look at two
statements which may not be consecutive in the source listing to determine the effect
of the I/O statement.
Many edit descriptors can be prefixed by a repeat count and suffixed with a field-
width, i.e. the total number of digets. Thus in the two examples given above, 2I and
3F10.6 could be described as two integers and three floating-point real numbers. The
fieldwidths of the of the numbers are the default fro the integers and 10 for the reals (a
description follows).
In general, if w is larger than the number of digets required to represent the number
leading spaces are added. If w is too small to represent the number then on output w
asterisks are printed and on input the leftmost w digits are read (truncating the
number so beware!).
The I/O statement will use as many of the edit descriptors as it requires to process all
the items in the I/O list. Processing will terminate at the next edit descriptor which
requires a value from the I/O list.
8.3.1 Integer
The edit descriptor I is used to control the format of integers and can have the form
Iw or Iw.m. Several integers may be read/written in the same format by including a
repeat count, i.e. aIw or aIw.m. For example:
It is important to remember that the decimal point is counted in the the width w of the
output. In the above example, although there are 7 numerals to write to the screen the
field width must be 8 (or larger) to cater for the decimal point.
eral real numbers may be read/written in the same format by including a repeat
count, i.e. aEw.d. If w is too large to represent the number leading spaces are added
before the digets. For example:
Both are used in the same way as the E descriptor, for example:
8.3.4 Character
The A edit descriptor is used to control the format of characters and strings. It has the
form A or Aw. The A descriptor will writes as many characters as required while Aw
writes a string of width w. If w is bigger than the character string leading spaces are
added before the string’s characters. For example:
CHARACTER(LEN=8) :: long=’Bookshop’
CHARACTER(LEN=1) :: short=’B’
...
WRITE(*,*) long ! Bookshop
WRITE(*,’(A)’) long !Bookshop
WRITE(*,’(A8)’) long !Bookshop
WRITE(*,’(A5)’) long !Books
WRITE(*,’(A10)’) long ! Bookshop
WRITE(*,’(A)’) short !B
WRITE(*,’(2A) short, long !BBookshop
WRITE(*,’(2A3) short, long ! BBoo
When using the A descriptor in formatted READ() statement (i.e. input) the character
string does not need to be enclosed in quotes.
8.3.5 Logical
Logical data is formatted using the L descriptor and has the form Lw or aLw for
repeated counts. Usuall only two forms of the L descriptor are used, L for the single
charater ‘T’ or ‘F’ format and L7 which allows ‘.TRUE.’ and ‘.FALSE’. to be input .
LOGICAL :: ltest=.FALSE.
WRITE(*,*) ltest ! F
WRITE(*,’(2L1)’) ltest, .NOT.ltest !FT
WRITE(*,’(L7)’) ltest ! F
Notice that spaces may be specified in the output line by either the X edit descriptor or
by spaces in a character string, as in ‘b = 201’.
INTEGER, DIMENSION(10) :: a
READ (*,*) a(1), a(2), a(3) !read values into 3 elements
READ (*,*) a !read in 10 values
READ (*,*) a(5:8) !read values into 4 elements
Array elements may only appear once in an I/O list, for example:
c= (/1,2,1/)
READ (*,*) b(c) !illegal
TYPE point
REAL :: x, y
END TYPE
TYPE (point) :: pt
TYPE triangle
TYPE (point) :: a, b, c
END TYPE
TYPE (triangle) :: tri
READ (*,*) pt
READ (*,*) pt%x, pt%y
...
READ (*,*) tri
READ (*,*) tri%a%x, tri%a%y, tri%b%x, tri%b%y, tri%c%x, tri%c%y
An object of a derived data type which contains a pointer (see later) may not appear in
an I/O list. This restriction prevents problems occurring with recursive data types.
where do_var is an interger (and cannot be a pointer). Consider the following exam-
ples:
INTEGER :: j
REAL, DIMENSION(5) :: a
READ (*,*) ( a(j), j=1,5) !a(1), a(2), a(3), a(4), a(5)
WRITE (*,*) ( a(j), j=5,1,-1) !a(5), a(4), a(3), a(2), a(1)
The first statement would read 5 values in to each element of a, in assending order.
The second statement would write all 5 values of a in reverse order.
The implied-do-list may also be nested
INTEGER :: I, J
REAL, DIMENSION(10,10) :: B
WRITE (*,*) ((B(I,J),I=1,10), J=1,10)
8.5 Namelist
A namelist is a facility for grouping variables for I/O. A NAMELIST is most useful for
output as this can be useful for program testing and debugging. It’s use on input is
slightly more complicated and is best considered elsewhere.
for example:
Variables must be declared before appearing in a NAMELIST group (and must not be a
mixture of PRIVATE and PUBLIC variables). The keyword NML= may be used in place
of the format specifier in an I/O statement. For example:
WRITE (*,NML=week)
This record format (including & and / characters) must be used for input.
Arrays may also be specified, for example
would produce
INTEGER :: i, j
...
WRITE(*,*,ADVANCE=’NO’) ’Enter i value: ’
READ(*,*) i
WRITE(*,*) ’Enter j value: ’ !’ADVANCE=’YES’
READ(*,*) j
If the user enters the values 10 and 20 this would appear on the screen as
Enter i value: 10
Enter j value:
20
The non-advancing I/O looks neat compared to the (default) advancing I/O which is
spread over two lines.
8.7 Exercises
1. What values would be read into the variables in the READ() statement in the
following:
REAL :: a, b, c
REAL, DIMENSION (1:5) :: array
INTEGER :: i, j, k
READ(*,*) a, b, c
READ(*,*) i, j, k, array
Check your answer by write a program which write the values of the variables
to the screen.
REAL :: a
CHARACTER(LEN=2) :: string
LOGICAL :: ok
READ (*,'(F10.3,A2,L10)') a, string, ok
what would be read into a, string and ok if each of the following lines were
typed as input records (where b represents a space or blank character)?
(a) bbb5.34bbbNOb.true.
(b) 5.34bbbbbbYbbFbbbbb
(b) b6bbbbbb3211bbbbbbT
(d) bbbbbbbbbbbbbbbbbbF
Check your answer by write a program which write the values of the variables
to the screen.
3 harry 50.9
When initialising arrays to hold the table’s values note that the header might be
stored as a string while the underline is simply a repeated character!. The first
column should be an interger, the second a string, the third a real number.
In the previous modules all input and output was performed from and to the default
devices, namely the keyboard and screen. In many circumstances this is not the most
appropriate action, i.e. temporary storage of large amounts of intermediate results;
large amounts of input or output; output from one program used as the input of
another; a set of input data which is used many times, etc.
A mechanism is required which permits a programmer to direct input to be per-
formed on data from a source other than the keyboard (during execution time) and to
store output in a more ‘permanent’ and capacious form. This is generally achieved by
utilizing the computer’s filestore which is a managed collection of files. A file such as
the source program or a set of I/O data is normally formatted, which means it consists
of an ordered set of character strings separated by an end of record marker. A format-
ted file may be viewed using an editor or printed on a printer. An unformatted file
(see later) has no discernable structure and should be regarded as single stream of
bytes of raw data. An unformatted file is normally only viewed using a suitable user
written program.
INTEGER :: nunit=10
...
READ(UNIT=10,*)
WRITE(UNIT=nunit,*)
The unit number may also be specified as a positional argument as shown later.
Certain unit numbers are reserved to for I/O with the keyboard and screen. The unit
number 5 refers to the keyboard (so you should never write to it!) while 6 refers to the
screen (so you should never read from it!). The following READ() statements are all
equivalent, as are the WRITE() statements:
Some computer systems have a naming convention which will “map” unit numbers
to default file names, for example when using unit number 10 on a VAX/VMS system
this will map to a file called FOR010.DAT and on some UNIX systems to a file called
fort.10.
Also some computer systems provide a form of external variable which may be
defined prior to execution and the contents of the variable used as a filename. Again
on a VAX/VMS system accessing unit 10 will cause an external variable FOR010 to be
checked for a filename.
System specific information such as this is provided in the language reference manual
for that system.
READ(clist) list
[UNIT=] unit-number,
[FMT=] format-spec
[,REC= record-number]
[,IOSTAT=ios]
[,ADVANCE=adv]
[,SIZE=integer-variable]
[,EOR=label]
[,END=label]
[,ERR=label]
Note that a unit-number and format-spec are required in that order (though the
keywords are not), the rest are optional. Most of the keywords are for advanced file
manipulation, and as such will not be discussed here.
A useful argument to detect errors in I/O, particularly to files, is IOSTAT. If an error
occurs during I/O, the variable specified by IOSTAT will return a positive, system
dependent integer. The value 0 will be returned if the operation completes success-
fully. For example:
WRITE(clist) list
[UNIT=] unit-number,
[FMT=] format-spec
[,REC= record-number]
[,IOSTAT=ios]
[,ADVANCE=adv]
[,SIZE=integer-variable]
[,EOR=label]
[,ERR=label]
Again note that a unit-number and format-spec are required in that order and
that other arguments are optional. IOSTAT remains one of the most useful arguments
and works the same for WRITE() as for READ() statements. For example:
OPEN(unit_no, [olist] )
where unit_no is a valid unit number specifier (with or without the keyword) and
olist is a list of keyword clauses (explained below) For example, the following
OPEN() statement all open a file associated with the unit number 10:
INTEGER :: ifile=10
...
OPEN(10)
OPEN(UNIT=10)
OPEN(UNIT=ifile)
The following keywords are some of those specified in the Fortran 90 language stand-
ard and may be used to specify the nature of the file opened:
OPEN (UNIT=10,FILE=’fibonacci.out’)
OPEN (UNIT=11,FILE=’fibonacci.out’,STATUS=’NEW’,IOSTAT=ios)
IF( ios/=0) THEN
WRITE(6,*) ’Error opening file: fibonacci.out.’
STOP
ENDIF
OPEN (UNIT=12, FILE=’student.records’, STATUS=’OLD’, &
FORM=’FORMATTED’, IOSTAT=ios)
If you are in any doubt about the default values for any of the fields of the OPEN()
statement, especially as some are machine dependent, specify the required values.
The combinations of possible error conditions, mean that careful thought should be
given to the specification of OPEN() statements and the associated error handling.
Specifying some argument values alter the default values of others while some combi-
nations of argument values are mutually exclusive, such problems are beyond these
notes.
For example:
CLOSE (10)
CLOSE (UNIT=10)
CLOSE (UNIT=nunit, IOSTAT=ios)
INQUIRE (inquiry-list)
FILE=fname
or
UNIT=unum
plus any the following (other keywords/arguments exist but are for more advanced
file I/O):
9.6 Exercises
1. Complete the following statement, which would open an unformatted file
called ‘result.dat’ that does not exist.
OPEN(UNIT=10,..)
2. Write a section of code which would open 5 files on the unit numbers from 20
to 25. The default values should be used for all keywords. Your code should
include a means of detecting errors.
3. Write sections of code to perform the following:
(a) test for the existence of a file called TEMP.DAT
(b) test if a file has been opened on unit 10.
(c) test to see if the file opened on unit 15 is a formatted or unformatted file.
The program fragments should output the results in a suitable form.
4. Write a Fortran program which will prompt the user for a file name, open that
file and then read the file line by line outputting each line to the screen prefixed
with a line number. Use the file which contains the source of the program as a
test file.
10 Dynamic arrays
So far all variables that have been used have been static variables, that is they have
had a fix memory requirement, which is specified when the variable is declared. Static
arrays in particular are declared with a specified shape and extent which cannot
change while a program is running. This means that when a program has to deal with
a variable amount of data, either:
• an array is dimensioned to the largest possible size that will be required, or
• an array is given a new extent, and the program re-complied every time it is run.
In contrast dynamic (or allocatable) arrays are not declared with a shape and initially
have no associated storage, but may be allocated storage while a program executes.
This is a very powerful feature which allows programs to use exactly the memory
they require and only for the time they require it.
They must include the ALLOCATABLE attribute and the rank of the array, but cannot
specify the extend in any dimension or the shape in general. Instead a colon (:) is used
for each dimension. For example:
n=10
ALLOCATE( a(100) )
ALLOCATE( b(n,n), c(-10:89) ).
The storage used by an allocatable array may be released at any time using the DEAL-
LOCATE statement:
DEALLOCATE ( a, b )
DEALLOCATE ( c, STAT=test )
IF (test .NE. 0) THEN
STOP ‘deallocation error’
ENDIF
It is good programming practice to deallocate any storage that has been reserved
through the ALLOCATE statement. Beware, any data stored in a deallocated array is
lost permanently!
AllOCATED( name )
or:
SUBROUTINE swap(a, b)
REAL, DIMENSION(:) :: a, b
REAL, ALLOCATABLE :: work(:)
ALLOCATE( work(SIZE(a)) )
work = a
a = b
b = work
DEALLOCATE( work ) !necessary
END SUBROUTINE swap
The automatic arrays a and b are static variables, the program allocates the required
storage when swap is called, and deallocates the storage on exiting the procedure.
The storage allocated to the allocatable array work must be explicitly deallocated to
prevent a memory leak.
Memory leaks are cumulative, repeated use of a procedure which contains a memory
leak will increase the size of the allocated, but unusable, memory. Memory leaks can
be difficult errors to detect but may be avoided by remembering to allocate and deal-
locate storage in the same procedure.
10.3 Exercises
1. Write a declaration statement for each of the following allocatable arrays:
(a) Rank 1 integer array.
(b) A real array of rank 4.
(c) Two integer arrays one of rank 2 the other of rank 3.
(d) A rank one real array with lower and upper bound of -n and n respectively.
2. Write allocation statements for the arrays declared in question 1, so that
(a) The array in 1 (a) has 2000 elements
(b) The array in 1 (b) has 16 elements in total.
(c) In 1 (c) the rank two array has 10 by 10 elements, each index starting at ele-
ment 0; and the rank three array has 5 by 5 by 10 elements, each index starting
at element -5.
(d) The array in 1 (d) is allocated as required.
3. Write deallocation statement(s) for the arrays allocated in 2.
4. Write a program to calculate the mean and the variance of a variable amount of
data. The number of values to be read into a real, dynamic array x is n. The pro-
gram should use a subroutine to calculate the mean and variance of the data
held in x. The mean and variance are given by:
n
mean = ∑ x ( i ) ⁄ n
i = 1
n
2
variance = ∑ ( x ( i ) – mean ) ⁄ ( n – 1 )
i = 1
11 Pointer Variables
11.2 Specifications
The general form for pointer and target declaration statements are:
Where:
• type is the type of data object which may be pointed to and may be a derived
data type as well as intrinsic types.
• attribute is a list of other attributes of the pointer.
A pointer must have the same data type and rank as its target. For array pointers the
declaration statement must specify the rank but not the shape (i.e. the bounds or
extend of the array). In this respect array pointers are similar to allocatable arrays.
For example, the following three pairs of statements, all declare pointers and one or
more variables which may be targets:
Where pointer is a pointer variable and target is any valid target. pointer may
now be used as an alias to the data stored by target. The pointer assignment opera-
tor also allocates storage required by the pointer.
To change the value of a pointer’s target (just like changing the value of a variable)
use the usual assignment operator (=). This is just as it would be for other variable
assignment with a pointer used as an alias to another variable.
The following are examples of pointer assignment:
INTEGER, POINTER :: pt
INTEGER, TARGET :: x=34, y=0
...
pt => x ! pt points to x
y = pt ! y equals x
pt => y ! pt points to y
pt = 17 ! y equals 17
pt => x x 34
y 0
pt
y = pt x 34
y 34
pt
pt => y x 34
y 34
pt
pt = 17 x 34
y 17
pt
Although this may appear to be a pointer pointing to another pointer, pt2 does not
point to pt1 itself but to pt1’s target. It is wrong to think of ‘chains of pointers’, one
pointing to another. Instead all pointers become associated with the same target.
Beware, of using the following statements, they are both illegal:
11.3.1 Dereferencing
Where a pointer appears as an alias to a variable it is automatically dereferenced; that
is the value of the target is used rather than the pointer itself. For a pointer to be deref-
erenced in this way requires that it be associated with a target.
Pointer are automatically dereferenced when they appear:
• As part of an expression.
• In I/O statements.
For example:
pt => a
b = pt !b equals a, pt is dereferenced
IF( pt<0 ) pt=0 !pt dereferenced twice
A pointer that has been nullified may be thought of as pointing ‘at nothing’.
The status of a pointer may be found using the intrinsic function:
The initial undefined status of the pointers is changed to associated by pointer assign-
ment, there-after the ASSOCIATED function returns a value of .TRUE. for both point-
ers. Pointer pt1 is then nullified and its status tested again, note that more than one
pointer status may be tested at once. The association status of pt2 with respect to a
target is also tested. Finally both pointers are nullified in the same (last) statement.
...
ALLOCATE( p, pa(n) )
...
DEALLOCATE( p, pa )
In the above example p points to an area of dynamic memory and can hold a single,
real number and pa points to a block of dynamic memory large enough to store 100
real numbers. When the memory is no longer required it may be deallocated using the
DEALLOCATE statement. In this respect pointers behave very much like allocatable
arrays.
grid(1:10,1:10)
centre(1:4,1:4)
row(1:10)
An array pointer can be associated with the whole array or just a section. The size and
extent of an array pointer may change as required, just as with allocatable arrays. For
example:
Note, an array pointer need not be deallocated before its extent or bounds are rede-
fined.
list(-5:5)
pt => list pt(-5:5)
list(-5:5)
pt => list(:) pt(1:11)
list(-5:5)
pt => list(1:5:2) pt(1:3)
The extent (or bounds) of an array section are determined by the type of assignment
used to assign the pointer. When an array pointer is aliased with an array the array
pointer takes its extent form the target array; as with pt => list above, both have
bounds -5:5. If the array pointer is aliased to an array section (even if the section cov-
ers the whole array) its lower bound in each dimension is 1; as with pt => list(:)
above, pt’s extent is 1:11 while list’s extent is -5:5. So pt(1) is aliased to list(-
5), pt(2) to list(-4), etc.
It is possible to associate an array pointer with an array section defined by a subscript
triplet. It is not possible to associate one with an array section defined with a vector
subscript, v above. The pointer assignment pt => list(1:5:2) is legal with
pt(1) aliased to list(1), pt(2) aliased to list(3) and pt(3) aliased to
list(5).
TYPE data
REAL, POINTER :: a(:)
END TYPE data
TYPE( data ) :: event(3)
DO i=1,3
READ(5,*) n !n varies in loop
ALLOCATE( event(i)%a(n) )
READ(5,*) event(i)%a
END DO
The number of values differs for each event, the size of the array pointer depends on
the input value n. When the data is no longer required the pointer arrays should be
deallocated:
DO i=1,3
DEALLOCATE( event(i)%a )
END DO
TYPE node
REAL :: item
TYPE( node ), POINTER :: next
END TYPE node
The derived type node contains a single object item (the data in the list) and a
pointer next to another instance of node. Note the recursion-like property in
the declaration allowing the pointer to reference its own data type.
Linked lists are a very powerful programming concept, their dynamic nature
means that they may grow or shrink as required. Care must be taken to ensure
pointers are set up and maintained correctly, the last pointer in the list is usually
nullified. Details of how to implement, use and manipulate a linked list can be
found in some of the reading material associated with these notes.
type and rank. dummy arguments that are pointer may not have the INTENT at-
tribute, since it would be unclear whether the intent would refer to the pointer
itself or the associated target.
• Pointer arguments to external procedures require INTERFACE blocks.
When both the actual and dummy arguments are pointers, the target (if there is one)
and association status is passed on call and again on return. It is important to ensure
that a target remains valid when returning from a procedure (i.e. the target is not a
local procedure variable), otherwise the pointer is left ‘dangling’.
When the actual argument is a pointer and the corresponding dummy argument is
not, the pointer is dereferenced and it is the target that is copied to the dummy argu-
ment. On return the target takes the value of the dummy argument. This requires the
actual argument to be associated with a target when the procedure is referenced.
For example:
PROGRAM prog
INTERFACE !needed for external subroutine
SUBROTINE suba( a )
REAL, POINTER :: a(:)
END SUBROUTINE suba
END INTERFACE
REAL, POINTER :: pt(:)
REAL, TARGET :: data(100)
...
pt => data
CALL suba( pt )
CALL subb( pt )
...
CONTAINS
SUBROUTINE subb( b ) !internal
REAL, DIMENSION(:) :: b !assumed shape of 100
...
END SUBROUTINE subb
END PROGRAM prog
INTERFACE
FUNCTION max_row ( a )
REAl, TARGET :: a(:,:)
REAL, POINTER :: max_row(:)
END FUNCTION max_row
END INTERFACE
REAL, TARGET :: a(3,3)
REAL, POINTER :: p(:)
...
p => max_row ( a )
...
Here the external function max_row returns the row of a matrix containing the largest
value. The pointer result is only allowed to point to the dummy argument a because it
is declared as a target, (otherwise it would have been a local array and left the pointer
dangling on return). Notice the function result is used on the right hand side of a
pointer assignment statement. A pointer result may be used as part of an expression
in which case it must be associated with a target.
11.10 Exercises
1. Write a declaration statement for each of the following pointers and their tar-
gets:
(a) A pointer to a single element of an array of 20 integers.
(b) A pointer to a character string of length 10.
(c) An array pointer to a row of a 10 by 20 element real array.
(d) A derived data type holding a real number three pointers to neighbouring
nodes, left, right and up (this kind of derived data structure may be used to
represent a binary tree).
2. For the pointer and target in the following declarations write an expression to
associate the pointer with:
(a) The first row of the target.
(b) A loop which associates the pointer with each column of the target in turn.
3. Write a program containing an integer pointer and two targets. Nullify and
report the initial status of the pointer (using the ASSOCIATED intrinsic func-
tion). Then associate the pointer with each of the targets in turn and output
their values to the screen. Finally ensure the pointer ends with the status ‘not
currently associated’.
4. Write a program containing a derived data type. The data type represents dif-
ferent experiments and should hold the number of reading taken in an experi-
ment (an integer) and values for each of the readings (real array pointer).
Read in the number and values for a set of experimental readings, say 4, and
output their mean. Deallocate all pointers before the program finishes.
5. Write an internal function that takes a single rank one, integer array as an argu-
ment and returns an array pointer to all elements with non-zero values as a
result. The function will need to count the number of zero’s in the array (use
the COUNT intrinsic), allocate the required storage and copy each non-zero
value into that storage. Write a program to test the function.
12 Intrinsic procedures
Fortran 90 offers many intrinsic function and subroutines, the following lists provide
a quick reference to their format and use.
In the following intrinsic function definitions arguments are usually named according
to their types (I for integer C for character, etc.), including those detained below.
Optional arguments are shown in square brackets [ ], and keywords for the argument
names are those given.
KIND - describes the KIND number.
SET - a string containing a set of characters.
BACK - a logical used to determine the direction a string is to be searched.
MASK - a logical array used to identfy those element which are to take part in the
desired operation.
DIM - a selected dimension of an argument (an integer).
CHAR( I [, KIND] ) - returns the Ith character in the machine specific collating
sequence.
IACHAR( C ) - returns the position of the character in the ASCII collating sequence.
ICHAR( C ) - returns the position of the character in the machine specific collating
sequence.
INDEX( STRING, SUBSTRING [, BACK] ) - returns the leftmost (rightmost if
BACK is .TRUE.) starting position of SUBSTRING within STRING.
LEN( STRING ) - returns the length of a string.
LEN_TRIM( STRING ) - returns the length of a string without trailing blanks.
LGE( STRING_A, STRING_B ) - lexically greater than or equal to.
IBITS( I, POS, LEN ) - extracts a sequence of bits length LEN from integer I
starting at POS
IBSET( I, POS ) - sets bit POS of integer I to 1.
IEOR( I, J ) - performas an exclusive .OR. on the bits of integers I and J.
IOR( I, J ) - performes an inclusive .OR. on the bits of integers I and J.
ISHIFT( I, SHIFT ) - logical shift of the bits.
ISHIFTC( I, SHIFT [, SIZE] ) - logical circular shift on a set of bits on the
right.
NOT( I ) - logical complement on the bits.
13 Further reading