Besides the main program and the
BLOCK DATA subprogram there
are two other kinds of complete program units: the external function and
the subroutine. Any FORTRAN 77 statement (except
BLOCK DATA and
PROGRAM) may appear in these
procedures and two statements,
only appear in them. Both must end with an
END statement. The
external function returns one value via the name of the function and the
subroutine may return zero or more values via an argument list. In a sense,
the external function is a specialised form of the subroutine.
Program units are independent in that they have their own symbolic names and labels. They may call each other but cannot call themselves, either directly or indirectly. (This is called recursion and it is not allowed in FORTRAN 77.) Information is passed to and from external procedures via three methods:
The first line of an external function declares the type and name of the function, as well as listing the dummy arguments.
type FUNCTION fname(dummy1, dummy2, …, dummyn)
The data type of the function type designates the data type of the
value returned by the external function. If it is omitted,
then the data type returned is determined by the first letter of the symbolic
name fname. If the external function returns a
CHARACTER value, then the length must be specified or given as
CHARACTER*(*). In this second case, the length will be as
specified in the invoking program unit.
The name fname may be used within the external function as a variable but it must be assigned a value before the end of the program unit. The last value assigned to the name fname is the value returned to the invoking program unit.
A function is invoked by using its name fname followed by parentheses
and an optional list of arguments called actual arguments
which correspond to the dummy arguments in the
statement. There may be zero or more dummy arguments of any data type which
are declared (if necessary) in the body of the external function.
However, the parentheses must be present even if there are no arguments.
Consider the following program consisting of a main program and an external function.
PROGRAM MAIN INTEGER I,N,NMAX PARAMETER(NMAX=10) REAL COEF(0:NMAX),HORNER,X 10 CONTINUE WRITE(*,*)'Enter the degree of the polynomial' READ(*,*)N IF (N .GT. NMAX) THEN WRITE(*,*)'Degree too large. Choose smaller value.' GO TO 10 END IF WRITE(*,*)'Enter the coefficients in ascending order' DO 20, I = 0,N WRITE(*,*)'Enter the value for coefficient ',I READ(*,*)COEF(I) 20 CONTINUE WRITE(*,*)'Enter the value of X' READ(*,*)X WRITE(*,*)'The value of the polynomial at ',X,' is ', $ HORNER(COEF,N,X) STOP 'End of program' END REAL FUNCTION HORNER(A,N,X) C This function returns the value of the polynomial C y = a_0 + a_1 x + a_2 x^2 + … + a_n x^n C using Horner's method. INTEGER I,N REAL A(0:N),X HORNER = A(N) D0 10 I = N-1,0,-1 HORNER = A(I) + HORNER*X 10 CONTINUE END
In this example,
the actual arguments in the function reference in the main program and
X are the dummy arguments in
FUNCTION statement. The main program invokes the function
An external function is supposed to return exactly one value via the function
name. However, it is permitted for the function to have 'side effects', that
is, for the function to alter the values of the actual arguments or of
variables appearing in
COMMON blocks. However, there are many
restrictions on how a function with side effects can be used in a program so
if it is necessary to alter arguments or variables in
blocks, it is best to use a subroutine instead.
The first line of a subroutine gives the name of the program unit and a listing of the dummy arguments. Unlike an external function, a subroutine does not have a data type because a subroutine returns any values through its argument list, not through its name.
SUBROUTINE subname(dummy1, dummy2, …, dummyn)
There may be zero or more dummy arguments of any data type which are declared (if necessary) in the body of the subroutine. Unlike external functions, however, the parentheses may be omitted if there are no dummy arguments.
Because the symbolic name subname is global in nature, it may not be used as a variable within the subroutine.
A subroutine is invoked with a
CALL statement in the invoking
CALL subname(arg1, arg2, …, argn)
The arguments argi in the
CALL statment are
known as actual arguments (as opposed to the dummy arguments
which appear in the
SUBROUTINE statement). If the subroutine
has no arguments, then the
CALL statement is simply
Consider the following program consisting of a main program and a subroutine.
PROGRAM MAIN REAL TIME,H,M,S 10 CONTINUE WRITE(*,*)'Enter the time in decimal hours' READ(*,*,END=999)TIME IF (TIME .LT. 0.0) GO TO 999 CALL CONVRT(TIME,H,M,S) WRITE(*,20)TIME,H,M,S 20 FORMAT(' ',F9.2,' hours = ',I6,' h ',I2,' m ',F5.2,' s') GO TO 10 999 STOP 'End of program' END SUBROUTINE CONVRT(DTIME,HOURS,MINS,SECS) C This subroutine converts DTIME (time in decimal hours) to individual C hours, minutes and seconds INTEGER HOURS, MINS REAL DSECS,DTIME,SECS C Convert DTIME from decimal hours to decimal seconds for ease of calculation. DSECS = DTIME*3600.0 C Calculate individual hours, minutes and seconds. HOURS = INT(DTIME) SECS = DSECS - 3600.0*HOURS MINS = INT(SECS/60.0) SECS = SECS - 60.0*MINS END
The main program consists of a repeat-until loop which is stopped if the
user enters a negative value or the EOF
character. (The EOF is system dependent
but should be something like <CTRL>C, <CTRL>D or <CTRL>Z.)
If a legal value is input for the variable
TIME, the subroutine
CONVRT is called with four arguments. The first argument,
TIME, already has a value when the subroutine is called. The
other three arguments,
will receive their values in the subroutine. After the
statement is executed, the
WRITE statement outputs the results
of the calculations in the subroutine and the loop repeats.
CONVRT begins with the
SUBROUTINE statement and assorted variable type declarations.
One of the variables,
DSECS, is local to the subroutine but
the rest are the arguments in the dummy argument list. The first value in
the dummy argument list is passed to the subroutine from the calling
program. Several calculations take place and values are assigned to the
other three arguments which are passed back to the calling program at the
end of the subroutine.
In this example,
S are actual arguments in the
CALL statement in
the main program.
SECS are the corresponding dummy arguments in the
One method for passing information between program units is via an argument
list. The list of dummy (or formal) arguments is specified in the
SUBROUTINE statements in the external
procedure. There may be zero or more dummy arguments in the list. If there
are no arguments, then the parentheses may be omitted from the
SUBROUTINE statements but not from the
function reference and the
Dummy arguments may be constants, variables, arrays, procedures or labels.
Dummy arguments may not appear in
EQUIVALENCE statements or
The actual arguments in the function reference or
are associated in a one-to-one fashion with the dummy arguments in the
external procedure, subject to the following restrictions:
CHARACTER, then its length must not be greater than that of the corresponding
CHARACTERvariable in the actual argument list.
Because program units are compiled separately, the compiler is unlikely to detect mismatched argument lists. This is a common source of run-time errors.
If a dummy argument is a variable which has a value assigned to it in the procedure, then the corresponding actual argument may be a variable, an array element or a character substring. However, if the dummy argument does not alter any input value, then the corresponding actual argument may also be a constant or expression.
Consider this snippet of code consisting of a main program and a subroutine:
PROGRAM MAIN INTEGER M,N M = 5 N = 20 CALL ADD(M,N) END SUBROUTINE ADD(I,J) INTEGER I,J J = I + J END
In this example,
N are assigned values in the
main program and passed to the subroutine
ADD. The subroutine
adds the two values and returns the sum in the second argument in the list.
Thus, before the subroutine call,
M = 5 and
N =20 but after the subroutine call,
been altered to the number 25.
Now consider a slightly different example.
PROGRAM MAIN INTEGER N N = 20 CALL ADD(5,N) END SUBROUTINE ADD(I,J) INTEGER I,J J = I + J END
The program works in exactly the same way although in this case, the first actual argument is a constant, the number 5. Since the first dummy argument is not altered in the subroutine, this is perfectly legal.
What happens in this case?
PROGRAM MAIN INTEGER M M = 5 CALL ADD(M,20) END SUBROUTINE ADD(I,J) INTEGER I,J J = I + J END
The second actual argument is a constant (the number 20) but the value of the second dummy argument is altered in the subroutine. What happens next is system-dependent. Some systems will detect that the program is attempting to overwrite a constant and stop with an error message, but other systems will actually overwrite the constant 20 with 25 so that anywhere you try to use the value 20, you will actually be using 25. Such errors are nearly impossible to track down so be careful when passing constants as actual arguments.
If the actual argument is an expression, that expression is evaluated before the procedure is referenced or called. As in the case with constant actual arguments, you should not attempt to alter the value of the corresponding dummy argument.
The second actual argument,
I**ABS(N), is evaluated before the
EVAL is called. The corresponding dummy argument
should be an input argument only and not get altered in the subroutine.
CHARACTER variables may be passed to a procedure along with
another variable denoting their lengths but it is usually preferable to have
the computer set the length automatically using the
specification in the procedure. When this is used, the length of the
CHARACTER argument is automatically set equal to the
length of the actual
PROGRAM MAIN … CALL INPUT('model01.dat',M,N,RADIUS,DENSE,X) … END SUBROUTINE INPUT(FNAME,NMAX,N,R,RHO,X) CHARACTER*(*) FNAME INTEGER N,NMAX DOUBLE PRECISION R(0:NMAX),RHO(0:NMAX),X(0:NMAX) OPEN(1,FILE=FNAME,STATUS='OLD',ERR=900) … END
The dummy argument
FNAME is of type
CHARACTER and is
of length 11, the same size as the corresponding actual argument, the
If a dummy argument is an array, then the corresponding actual argument must either be an array name (without a subscript) or an array element (with a subscript). In the first instance, the entire array is passed. In the second, only part of the array is passed, starting with the position denoted in the actual argument.
Consider a program consisting of main program unit and a subroutine which multiplies element-wise two arrays of length 100 and returns the product in a third array.
PROGRAM MAIN REAL A(100),B(100),C(100) … CALL MULT(A,B,C) … END SUBROUTINE MULT(X,Y,Z) INTEGER I REAL X(100),Y(100),Z(100) DO 10, I = 1,100 Z(I) = X(I)*Y(I) 10 CONTINUE END
In this example, entire arrays are passed to and from the subroutine.
It is more often the case that the dimension of the array is also passed
with the array name. This makes the procedure more generalised so that it
can work on arrays of any size. When an array appearing in the dummy
argument list of in an external procedure is declared using an input value for
the dimension, this is known as an adjustable size declaration.
Note that adjustable size declarations can be made only for arrays appearing
as dummy arguments in external procedures, not for arrays which are local to
the proceudre. Also note that the variables being used as array dimensions
must be declared as
INTEGERs before they are used in the
adjustable size declaration.
PROGRAM MAIN INTEGER N,NMAX PARAMETER(NMAX=100) REAL A(NMAX),B(NMAX),C(NMAX) … CALL MULT(N,A,B,C) … END SUBROUTINE MULT(SIZE,X,Y,Z) INTEGER I,SIZE REAL X(SIZE),Y(SIZE),Z(SIZE) DO 10, I = 1,SIZE Z(I) = X(I)*Y(I) 10 CONTINUE END
In this example, the actual array size
N (which may be less than
or equal to the maximum-allowed size
NMAX) is passed to the
subroutine along with the array names. Now the procedure will work on any
Sometimes it isn't known ahead of time how large the array will be. Either
the maximum size of the array can be passed to the procedure or the array
appearing in the dummy argument list can be declared as an
assumed size array. In this instance, the upper bound of
the last dimension of the array may be declared using as asterisk
SUBROUTINE DERIV(X,H,N) INTEGER N REAL H,X(*) X(N) = (X(N+1) - X(N-1))/(2.0*H) END
All that is known about array
X is that its lower bound (or
subscript) is 1. It is up to the programmer to ensure that the subroutine
is never called if the value of
N is out-of-bounds.
All of these discussions hold for multi-dimensional arrays and arrays whose lower bound is not 1. Multi-dimensional arrays are a little tricky; it is usually sensible to pass the declared sizes of the arrays to the procedure in order to preserve the storage scheme of the array. Only the upper bound of the last dimension may be assumed size or less than the actual declared size.
PROGRAM MAIN INTEGER MMAX,NMAX,M,N PARAMETER(MMAX=4,NMAX=3) REAL A(MMAX,NMAX),X(NMAX),Y(MMAX) … CALL MATVEC(MMAX,M,N,A,X,Y) … END SUBROUTINE MATVEC(DIM1,M,N,MATRIX,VECX,VECY) C Multiply a matrix by a vector. INTEGER DIM1,I,M,N READ SUM,MATRIX(DIM1,*),VECX(*),VECY(*) DO 20, I = 1,M SUM = 0.0 DO 10, J = 1,N SUM = SUM + MATRIX(I,J)*VECX(J) 10 CONTINUE VECY(I) = SUM 20 CONTINUE END
In the subroutine, the first dimension of the two-dimensional array is set to the declared size in the main program. This will preserve the integrity of the storage scheme.
It is possible to transfer sections of arrays by using array elements in the actual argument list and array names in the dummy argument list. This works because FORTRAN 77 passes arguments by reference rather than by value. That is to say, it is the storage address of the symbolic name that is passed, not the actual value(s) stored in the symbolic name.
PROGRAM MAIN INTEGER NMAX PARAMETER(NMAX=5) DOUBLE PRECISION ARRAY(-NMAX:NMAX) … CALL FLUMOX(ARRAY(-2)) … END SUBROUTINE FLUMOX(A) DOUBLE PRECISION A(*) … END
In this example, the actual argument is an array element and the dummy
argument is an array. What is passed to the subroutine is the address of
storage space of
ARRAY(2) and what the subroutine sees is this:
So only a part of the array is transferred and the subroutine effectively
thinks it's getting an array with 8 elements in it, starting with the
storage space assigned to
ARRAY(-2) in the main program.
This can make your code rather obscure so use this trick with caution.
EXTERNAL statement names external procedures which are
required in order to run a given program unit. It is required any time an
external procedure or dummy procedure is used as an actual argument of
another procedure call. It is also necessary when calling a user-defined
procedure which has the same name as an intrinsic function. The general
form of the statement is
EXTERNAL ename1, ename2, …, enamen
where ename is the name of an external function, subroutine or dummy procedure in an argument list.
Consider the following program fragment consisting of a main program, a
POLY and a subroutine
The main program calls the subroutine
BISECT with the function
POLY as an actual argument.
PROGRAM MAIN REAL A,B,EPS,POLY,X EXTERNAL POLY … CALL BISECT(POLY,A,B,X,EPS) … END REAL FUNCTION POLY(X) REAL X POLY = X**2 - X - 2.0 END SUBROUTINE BISECT(FUNC,XLOWER,XUPPER,XZERO,TOL) C This subroutine uses the bisection method to find a known root of the C external user-supplied function FUNC in the interval [XLOWER,XUPPER]. C The root is returned in XZERO. The tolerance for the interval size is C TOL and is defined by the calling program unit. INTEGER I,NMAX REAL FUNC,FX,TOL,XL,XLOWER,XU,XUPPER,XZERO PARAMETER(NMAX=25) C Error trapping for incorrect interval. IF (XLOWER .GE. XUPPER) THEN WRITE(*,*)'Incorrect interval. No zero found.' RETURN END IF C Initialise local copies of bounds so original values are unaltered. XL = XLOWER XU = XUPPER C Bisection method. Perform at most NMAX iterations. DO 10, I = 1,NMAX C Set midpoint of current interval and evaluate function at midpoint. XZERO = 0.5*(XL + XU) FX = FUNC(XZERO) C If root is found or interval is 'small enough', return. IF (FX .EQ. 0.0 .OR. 0.5*(XU-XL) .LT. TOL) THEN RETURN C Root is in the lower half of the current interval. ELSE IF (FX*FUNC(XL) .LT. 0.0) THEN XU = XZERO C Root is in the upper half of the current interval. ELSE XL = XZERO END IF 10 CONTINUE WRITE(*,*)'WARNING: Tolerance not met after ',NMAX,' iterations' END
POLY is the name of an external function and it
appears as an actual argument in the
statement in the main program, it has to be declared as
in the main program.
INTRINSIC statement is related to the
statement. It is used to declare a name to be that of an
intrinsic function but is necessary only if
that function is used as an actual argument in another procedure call. The
general form of the statement is
INTRINSIC iname1, iname2, …, inamen
where iname is the name of an intrinsic function.
The name of the intrinsic function being used as an actual argument must
be the specific name of the function and not the
generic name. Only some of the numerical functions have
specific names. The other functions, such as those for type conversion as
MIN, do not have specific names
and cannot appear in argument lists.
PROGRAM MAIN REAL PI PARAMETER(PI=3.14159265) INTRINSIC COS,SIN WRITE(*,*)'Cosine Function' CALL TABLE(COS,-PI,PI) WRITE(*,*)'PAUSE: Press <Enter> to continue' READ(*,*) WRITE(*,*)'Sine Function' CALL TABLE(SIN,-PI,PI) STOP 'End of program' END SUBROUTINE TABLE(F,LOWER,UPPER) INTEGER I,NMAX REAL DELTAX,F,LOWER,UPPER,X PARAMETER(NMAX = 100) DELTAX = (UPPER - LOWER)/NMAX DO 10, I = 0,NMAX X = LOWER + I*DELTAX WRITE(*,20)X,F(X) 10 CONTINUE 20 FORMAT(' ',F10.6,2X,F10.6) END
The intrinsic functions
SIN are passed to
a subroutine called
TABLE and so must be declared as
INTRINSIC in the calling program unit (the main program in this
case). Note that
SIN are the specific
names of the
REAL intrinsic functions of cosine and sine.
RETURN statement is simply
without any arguments or qualifiers. When encountered in a procedure, it
immediately transfers execution back to the calling program. The
END statement also has the same effect so it is unnecessary
(although not incorrect) to include a
RETURN statement at the
bottom of a procedure. However, sometimes it is necessary to have more than
one exit point from a procedure, perhaps for error-trapping purposes.
SUBROUTINE QUAD(A,B,C,X1,X2,FLAG) C This subroutine calculates the real roots of the quadratic equation C a x^2 + b x + c = 0 C using the method of Forsythe, Malcolm and Moler to avoid catastrophic C floating point cancellation. If the equation is linear or if complex C roots are encountered, the error flag is set to TRUE and no roots are C calculated. LOGICAL FLAG REAL A,B,C,DISC,X1,X2 C Calculate discriminant. DISC = B*B - 4.0*A*C C Set error flag. FLAG = DISC .LT. 0.0 .OR. A .EQ. 0.0 C If error flag is TRUE, return early. IF (FLAG) THEN WRITE(*,*)'ERROR! Complex roots or non-quadratic equation.' RETURN END IF C Otherwise, calculate roots. X1 = - (B + SIGN(SQRT(DISC),B))/(2.0*A) X2 = C/(A*X1) END
In this subroutine, the subroutine exits early if an error occurs.
Otherwise, execution returns to the calling program when the
statement is reached.
Under normal circumstances, local variables declared within a procedure
become undefined as soon as control passes back to the calling program.
However, it is sometimes useful or even necessary to preserve the values of
the local variables between calls or references to the procedure. This
is done with the
SAVE item1, item2, …, itemn
saves the values of each itemi listed and
saves the values of all allowable items in the procedure. The items may be
variables, array names or entire named
COMMON blocks but
not anything which appears as a dummy argument or in a blank
COMMON block. The
SAVE statement also may be
used in the main program but it has no effect.
SUBROUTINE MODEL(A,B,C,X,Y,Z) … LOGICAL FIRST SAVE FIRST DATA FIRST /.TRUE./ C Execute this block only the first time the subroutine is called. IF (FIRST) THEN FIRST = .FALSE. … END IF C Execution starts here on subsequent calls to the subroutine. … END
In this example, there is a certain block of statements which must be
executed the first time the subroutine is called but not on subsequent
calls. To do this, a local
LOGICAL variable called
FIRST is initialised to
.TRUE. in a
DATA statement so that it has appropriate value the first
time the subroutine is called. On the first call to the subroutine,
IF is executed, one of the statements being
FIRST = .FALSE. Local variables become undefined as
soon as the subroutine is exited but the
SAVE statement keeps
the current value of
FIRST between calls to the subroutine.
IF statement will not execute on any call to the
subroutine except the first.
A function or subroutine is usually entered at the beginning of the
program unit but it is possible to enter them at other points using the
ENTRY statement. The
ENTRY statement is a
nonexecutable statement which looks very much like a
statement and may occur one or more times within a subroutine or external
function. It may not appear within a
DO loop or
IF construct as this would have the effect of jumping
into the middle of these structures (which is not allowed).
ENTRY ename(dummy1, dummy2, …, dummyn)
This statement is difficult to use correctly and its use is strongly discouraged.
Consider the program fragment:
PROGRAM MAIN REAL A,B,C,D,E,F … CALL SUB(A) … CALL SUB1(B,C) … CALL SUB2(D,E,F) … END SUBROUTINE SUB(X) REAL X,Y,Z WRITE(*,*)'The value of X is ',X RETURN ENTRY SUB1(X,Y) X = X + Y RETURN ENTRY SUB2(X,Y,Z) X = X + Y + Z RETURN END
In the subroutine
SUB, the names
SUB2 define alternate entry points to the subroutine. If
the subroutine is called in the usual manner, then the first executable
WRITE(*,*)'The value of X is ',X.
However, if the subroutine is called at the entry point
then the first executable statement is
X = X + Y. Similarly, if the subroutine
is called at the entry point
SUB2, then the first executable
X = X + Y + Z.
The rules for using the
ENTRY statement are complicated,
particularly in external functions. It is usually better to use separate
subroutines or functions than to create multiple entry points in a single
subroutine or external function.
Not only is it possible to enter a subroutine in different places, it is
possible to return from a subroutine in different places via the
RETURN statement. The
RETURN returns control to the calling program
unit to a labelled statement after the completion of the
statement. Note that the alternate
RETURN may be used in
subroutines but not external functions.
This statement leads to a lack of modularity and its use is strongly discouraged.
Consider the program fragment:
PROGRAM MAIN … CALL CONFUS(A,B,C,*13,*66,*42) … 13 A = -A … 42 WRITE(*,*)'Default return' … 66 B = A - C … END SUBROUTINE CONFUS(X,Y,Z,*,*,*) … IF (X .LT. 0.0) RETURN 2 … END
In the main program, the subroutine
CONFUS is called with six
C and three
statement labels, each preceded by an asterisk
subroutine also contains six arguments,
Z and three asterisks
* which correspond to the
three label arguments in the calling program. In the subroutine, if
X < 0, then control is passed back to the main
program and execution resumed at the statement associated with the second
label in the argument list. In this example, that is the statement with
66 which is
B = A - C.
If none of the alternate
RETURN statements are used
RETURN 2 and
RETURN 3 in the given example), then control will be
returned to the main program in the usual way.
RETURN can easily be replaced with some kind
INTEGER flag which is returned via the argument list and an
IF statement in the calling program which decides what action
to take according to the status of the flag.