Chapter 10
Essentials of Essentials of Fortran-90/95/2003
Procedures (Subprograms)

Math 60 -- D. C. Smolarski, S.J.
Santa Clara University, Department of Mathematics and Computer Science

[Return to Math 60 Homepage | Return to Brief Contents Page]
[Return to Full Contents Page]

Contents


10.1 Overview

There are two types of procedures (that is, subprograms or self-standing program segments) in Fortran, subroutines and functions.

Fortran-90/95/2003 permits functions or subroutines to call themselves recursively, a feature not implemented in earlier versions of Fortran. Recursive procedures will be dealt with separately in the next chapter (cf. Chapter 11).

10.2 Subroutines

In writing a subroutine, one uses a special SUBROUTINE header statement to begin the code (corresponding to a PROGRAM statement in the main program), and uses a RETURN statement as the last logical statement in the code (corresponding to the STOP statement in the main program). The following is an example:
	SUBROUTINE name ( formal argument list )
	  .
	  .
	  .
	RETURN
	END
NOTES:
a) RETURN is used in subroutines and functions instead of STOP. The RETURN signifies the logical end of the subprogram. As with the STOP, there may be more than one RETURN in a subroutine, although multiple RETURNs are marked as an "obsolescent" feature in Fortran-95 and should be avoided. As with the STOP statement, Fortran-90/95/2003 permits omitting the RETURN if it comes immediately before the END.

b) The formal argument list, enclosed in parentheses after the subroutine name, contains only the variable names (separated by commas). The types and array dimensions are declared internally within the subroutine.

c) All variables are local and should be (normally) declared explicitly within a subroutine, particularly when coding subroutines "externally" (see section 10.3.3). As noted in the previous chapter on modules (section 9.1), there are no global variables in Fortran (unless one makes use of modules). Therefore, in particular, arrays that are parameters must be explicitly re-declared. (Exceptions to this declaration rule occur when making use of the Fortran-90/95/2003 features of "internal" procedures (see section 10.3.1) or modules for declaring variables.)

d) All statement numbers (e.g., used in FORMAT statements) are also local, i.e., independent of numbers in the main program or any other subprogram.

e) In cases where the subprogram is totally self-contained, for example where it generates and/or prints information and neither receives nor gives information to the calling program, the argument list and the enclosing parentheses are omitted.

To invoke a subroutine from the main program (or from any other subprogram), one uses the key word CALL, followed by the name of the subroutine, followed by actual arguments (separated by commas) enclosed in parentheses. For example,
	CALL name ( actual argument list )

The actual arguments used in the CALL statement and the formal (or "dummy") arguments used in the definition statement must match in number, order, and type. Switching the order of the arguments, neglecting to declare the data type of any variables used as arguments, or omitting arguments can lead to errors (exceptions to this principle are noted in Chapter 12). Sometimes the errors or inconsistencies may be caught by the compiler before execution, but at other times they are not, resulting in incorrect answers due to hard-to-detect causes.

The argument passing scheme used in Fortran is such that the values of the actual arguments will always be changed if the formal argument values are changed in the subprogram code. See section 10.7 below for more details.

NOTE: If the subroutine has no formal argument list (cf. Note e above), then there is no actual argument list (nor any parentheses) in the CALL statement.

10.3 Location of Procedure Code

There are three majors ways to associate the code for subroutines or functions with a program segment which will invoke them. They are:

10.3.1 Internal Procedures

Procedures may be included as internal to a specific main program (or other subprogram) by placing the subprogram code within the calling program unit, immediately before the END statement. If this is done, immediately before the subprogram code, a special CONTAINS statement is added.

Thus, as an example, one might have:

     PROGRAM SAMPLE2
        ! main program code
	STOP ! last main program statement
     CONTAINS
        SUBROUTINE ONE
        ! subprogram code
	RETURN
	END SUBROUTINE
	SUBROUTINE TWO
	! subprogram code
	RETURN
	END SUBROUTINE
     END PROGRAM
In this approach, the procedures have complete access to all variables and other entities declared in the main program (as an exception to the older Fortran rule). Thus a variable declared in the main program may be changed by an action in the subprogram without the necessity of passing the variable as a argument.

Any subprograms thus declared may not themselves have other internal subprograms.

Although this approach avoid needing parameters, it does not permit the subprogram to be shared among several different main program (or other subprograms).

10.3.2 Module Procedures

It is also possible to enclose procedures within a module and then import the module into a main program (or other procedure) via a USE statement. The definition of any subroutine or function within this module take precedence over other definitions that may conflict with the routine names. This approach helps to make explicit to the programmer from where the subprogram code comes.

When including procedures in a module, the code must occur after a CONTAINS statement as in the example below.

In this approach, the procedures do not have access to variables or other entities in the calling code.

The following is an example of program which uses a module subroutine:

        MODULE ONE
        CONTAINS
          SUBROUTINE TWO(A,B)
          ! body of subroutine code
          END SUBROUTINE TWO
        END MODULE
        PROGRAM TEST3B
          USE ONE
          ! body of main program
        END PROGRAM TEST3B

10.3.3 External Procedures

The oldest method of associating procedures with main programs (or other subprograms) is to merely include the code for all procedures after the main program code in the source code file or in independent auxiliary files (which are compiled and linked after the main program code. Such a subprogram is often called an external subroutine or function. No specific additional information is needed in most cases. (Noted below is the precautions that need to be taken with the data type of any FUNCTION if this method is used.)

The following is an example of program which uses this method of subprogram association:

        PROGRAM TEST3B
         ! body of main program
        END PROGRAM TEST3B
        SUBROUTINE TWO(A,B)
         ! body of subroutine code
        END SUBROUTINE TWO

10.4 Subroutine Example

The following code is an example of a Fortran subroutine that calculates the two roots of a second degree polynomial, given its coefficients, by using the quadratic formula.
	SUBROUTINE ROOTS(A,B,C,PROOT,NROOT)
	REAL A,B,C,PROOT,NROOT,RAD
	RAD=SQRT(B*B-4.0*A*C)
	PROOT=(-B+RAD)/(2.0*A)
	NROOT=(-B-RAD)/(2.0*A)
	RETURN
	END
This code would be associated with the calling segment in any of the three ways already mentioned and, in the calling segment, to invoke this subroutine, one would find statements such as:
	...
	READ(5,101)A,B,C
	CALL ROOTS(A,B,C,X1,X2)
	WRITE(6,102)X1,X2
	...
NOTE: the formal (or ``dummy'') arguments PROOT and NROOT may be termed "output only" arguments (since they convey no input information to the subroutine), and correspond to the actual arguments X1 and X2.

10.5 Arrays As Arguments

If a programmer wishes to pass an entire array, only the name of the array without any subscripts is listed, both in the calling statement and in the procedure code. Nevertheless, (except for internal subprograms without parameters) the array must be declared as an array both in the calling program and inside the subprogram. As an example, the following program sums the values stored in an array of size 10.

	PROGRAM MAIN
	REAL :: B(10),TOTAL
	...
	CALL SUMARR(B,TOTAL)
	...
	END
	!
	SUBROUTINE SUMARR(A,SUM)
	REAL :: A(10),SUM
	INTEGER I
	SUM = A(1)
	DO I=2,10
  	  SUM=SUM+A(I)
	END DO
	RETURN
	END

A programmer may want to use the same subprogram for arrays of different sizes. This can cause problems, however, since some Fortran compilers demand that the formal (subprogram) and actual (calling program) arrays be dimensioned exactly the same, eliminating the possibility of the subprogram array corresponding to two differently sized arrays in the calling program. To solve this dilemma, two general schemes are used. The older scheme involves passing the array dimension as one of the arguments. The newer scheme makes use of Fortran-90/95/2003 ``assumed-shape'' arrays.

In the first scheme, in the subprogram the array used as an argument is declared with a variable as the upper limit, for example,

	REAL :: A(N)
where both A and N are arguments passed into the subprogram.

NOTE: this type of indefinite dimensioning is only allowed in subprograms, and only for arrays which are arguments!!

For two and higher dimensional arrays, all the subscripts may be variables, but all these variables must be arguments passed from the calling program.

As an example of using this method, the following subroutine calculates the average of the numbers stored in an array.

	SUBROUTINE AVER(ARR,N,AVE)
	REAL :: ARR(N),AVE,SUM
	INTEGER :: N,I
	SUM=ARR(1)
	DO I=2,N
	  SUM=SUM + ARR(I)
	END DO
	AVE=SUM/N
	RETURN
	END

And in the calling program, one might have,

	CALL AVER(GRADE,25,AVERAG)
and elsewhere GRADE could be declared as follows:
	REAL GRADE(25)
Fortran-90/95/2003 has introduced an alternative to this method of indicating the dimension of array used as arguments. Such an array is called an assumed-shape array and is declared by using a colon (or colon with a preceding initial dimension value) in the declaration for each dimension of the array. For example, if D is an three-dimensional array being passed to a subroutine SUB1, the array could be declared as follows:
        SUBROUTINE SUB1(D)
	INTEGER, DIMENSION (:,:,:) :: D
In a case such as this, array D receives the exact values of the three dimensions from the program segment which invokes SUB1. This is done at compile time when the compiler checks the array used as the actual argument.

This method of generalizing the size of the array being passed to the subprogram forces the compiler to determine additional information it does not available have from the code itself (as when the dimension is declared either with a constant or a variable being passed as a argument). As a result, the subprogram code must be integrated with the calling program in special ways. This may be done either by making such a subprogram an internal subprogram (cf. section 10.3.1 above), or by using a module (cf. section 10.3.2), or by including an interface block (cf. section 9.6) if the subprogram is written in external form (cf. section 10.3.3).

One includes an interface block in the calling program segment by placing it after any variable declarations. The interface itself only contains the header line of the subprogram, declaration statements for the arguments, and the subprogram END statement. For example,

        PROGRAM SAMPLE1
	  INTEGER, DIMENSION (4, 5, 6) :: A
	  ...
	  INTERFACE
	     SUBROUTINE SUB1(D)
	       INTEGER, DIMENSION (:,:,:) :: D
	     END SUBROUTINE 
	  END INTERFACE
	  ...
	  CALL SUB1(A)
	  ...
	END PROGRAM
An example of a subprogram which uses assumed shaped arrays as well as array-handling features of Fortran-90/95/2003 (cf. sections 9.11, 9.13) is the following subroutine which exchanges the values contained in two one-dimensional arrays (i.e., vectors).
        SUBROUTINE SWAP(A,B)
	REAL, DIMENSION(:)       :: A, B
	REAL, DIMENSION(SIZE(A)) :: WORK
	WORK = A
	A = B
	B = WORK
	END SUBROUTINE

10.6 Fortran User-Defined Functions

In theory, Fortran user-defined functions are similar to Fortran library functions and to user-defined functions in other languages. To write code for a user-defined function, one follows the same rules as for a subroutine but substitutes the word FUNCTION for SUBROUTINE in the header statement. The key word FUNCTION can be preceded by type name if one wishes to supersede the default variable typing or if one simply wishes to be explicit.

For example,

	optional type FUNCTION name ( formal argument list )
	...
	RETURN
	END
Since there may be other keywords placed before the keyword FUNCTION, some authors recommend including the type within the variable declaration, explicitly giving the function name a specific data type. Using this approach, one would have:
	FUNCTION name ( formal argument list )
	data type of function name
	< declarations of arguments and other variables >
	...
	RETURN
	END

NOTES:

a) In general, all the rules mentioned above for Fortran subroutines also apply to Fortran functions.

b) Within the function, the function name is a local variable. Since functions are program units that return a specific value, the name (as a variable) must be given a value somewhere within the code of the function and this is the return value for the entire function when it is finished.

c) Unless designated as such, Fortran functions are not recursive, so the function should never attempt to call itself within the program code. Information about designating functions as recursive is given in the next chapter.

One could rewrite part of the sample subroutine in section 10.4 as a Fortran function as follows:

	FUNCTION PROOT(A,B,C)
	REAL PROOT
	REAL A,B,C
	PROOT = (-B+SQRT(B*B-4.0*A*C))/(2.0*A)
	RETURN
	END
(This is named PROOT for PLUS-ROOT, since the plus option was chosen in the computation of the root.)

Note that one could combine the first two lines of code into one line:

       REAL FUNCTION PROOT(A,B,C)

NOTE: Since a function takes on a specific value, it must be of a specific data type. That type should normally be explicitly listed in the function's code in either way shown above.

Then, in the calling section, one could use the function with its arguments (similar to the way the library functions such as SQRT are used within an arithmetic statement), for example,

	...
	READ(5,101)A,B,C
	X1=2.0*PROOT(A,B,C)
	...
The function's data type also plays an important rule in the calling section. If one uses the external method to associate subprograms with calling code (cf. section 10.3.3 above), one needs to convey to the calling segment the exact data-type of any user-defined function (unless one is using implicit typing). Thus, in any segment of code (main program, subroutine, function) which uses ("calls") a user-defined function, the function name should also be declared as a local variable in a standard type statement to avoid any type-mismatch errors. For example, in the section which uses the function PROOT given above, there should be a declaration statement,
	REAL PROOT, A, B, C
If one regularly includes an IMPLICIT NONE statement (as is recommended as good practice by various authors), one will get a compiler error if a user-defined function name is not explicitly declared.

10.7 Argument Passing In Fortran

When analyzing the various major computer languages, four principal methods to communicate via (or to "pass") arguments between a calling program and a subprogram can be identified. The standard names for these methods are:

In some languages (e.g., C++, Ada, Pascal), the programmer has the choice of determining the passing scheme for each argument.

In other languages (e.g., Fortran, C), the user has no choice. The passing scheme is predetermined, but sometimes may be different for arrays than for scalars.

In Fortran, the specific passing methods are predetermined by the implementation, and no choice by the user is possible. In Fortran, the actual arguments are always changed in the calling segment, if the formal arguments were changed in the subprogram segment. But the methods used to produce the change in value may vary from implementation to implementation.

Most implementations of FORTRAN-IV and some implementations of Fortran-90/95/2003 use

call by result (value-result) for simple (scalar) variables
and
call by reference (address) for arrays

Most implementations of FORTRAN-77 and other implementations of Fortran-90/95/2993 use

call by reference for all arguments (scalars or arrays).

In call by result (value-result), the subprogram copies values from the actual arguments in the calling program. But the subprogram works with values in independent memory locations and the argument values may be changed independently of the variables originally associated with them in the calling program. When the subprogram is finished (i.e. when the RETURN or END statement is reached), the associated variables in the calling program are changed to correspond to the values the arguments received in the subprogram.

In call by reference (address), no new memory locations are allocated. Instead, a link is set up so that whenever a subprogram variable is referenced, the corresponding variable in the calling program is accessed and changed. (This scheme may result in one memory locations having two different "names" [one used by the main program and one by the subprogram], a phenomenon often termed "aliasing.")

Note that in both of these calling conventions, the variables can be changed upon completion of the subprogram. This is not true of the call-by-value arguments (used in C, C++, or Pascal).

In call by value, the subprogram sets up new independent memory locations, copies the values from the actual arguments (as in call-by-value-result), but does not change any variables in the calling program upon its completion.

To show that problems may occur if a programmer is unclear as to which argument passing scheme is used, and thus, to what is happening exactly between the main program and the subroutine, let us examine the following example, written in a pseudo-language.

	function icrazy(i,j,k):integer
	i=j+k
	j=j+k
	icrazy=i+j+k
	k=j
	return
	end
Suppose the main program code included the following:
	n = 5
	ivalue = icrazy(n,n,n)
	ivalue = ivalue + n
	write (ivalue)
Then, depending on the type of "calling" method used, one gets three different answers!!

In the call by value case, the n in the calling code remains unchanged, whereas using call by value-result it changes. In the call by address case, the one memory location identified by n in the main program has three additional names in the function, i, j, and k. Therefore, whenever any one of them changes, the other three change values as well!

Since there is no uniformity as to how Fortran-90/95/2003 compilers actually pass scalar arguments, one should either test the argument passing scheme via some short program or avoid passing the same actual argument to different dummy arguments.

10.8 One Statement FUNCTIONS

It is also possible to define a one-line "statement function" at the beginning of each program segment in Fortran. After the type declaration statements and other initial statements, and before the first executable statement, one may define a statement in one line, similar to the typical mathematical statement definition of a function. For example,
	F(X) = X*X
or
	G(X) = 2.0*X**3 + 3.0*X*X + 4.0*X - 2.5
Then, in the code, one uses these functions as one would use any other functions. For example,
	...
	A = F(2) - 3.0*G(3)
	FPX=(F(X+DELTAX)-F(X))/DELTAX
	...
The data type of the function ought to be explicitly declared and must be declared when using an IMPLICIT NONE statement.

Since statement functions are local to the program segment in which they are defined and cannot be passed as parameters, most authors recommend that programmers use regular functions (or even internal functions) and discourage their use.

Statement functions have been listed as an obsolescent feature in Fortran-95.

10.9 Functions as Arguments; External and Intrinsic Statements

One may wish to use the same subprogram code with several different functions (either intrinsic [i.e., library] or user-defined), for example, a subprogram that performs numerical definite integration of an arbitrary function.. In these situations it would be useful if one could pass a function name as an argument to the subprogram. Passing function names as arguments is permitted in Fortran if one lists the various functions which will be passed as arguments in an EXTERNAL or an INTRINSIC statement in the calling segment.

One uses an EXTERNAL statement if the function is user-defined.

In FORTRAN-77, one uses INTRINSIC if the function is part of the Fortran library functions. In Fortran-90/95/2003, one must use an INTRINSIC statement if there are specific intrinsic functions not defined by the standard but are specific to a local implementation, although it may also be used with other intrinsic functions (or omitted).

These specification statements are located with the other specification statements, before the first executable statement in the calling segment.

For example,

	EXTERNAL F1,F2,F3
	INTRINSIC SIN,COS
	...
	CALL MYSUB1(SIN,X,Y,OUT1)
	CALL MYSUB1(COS,X,Y,OUT2)
	CALL MYSUB2(F2,Y)
	CALL MYSUB2(F3,Z)
	...

Inside a procedure (subroutine or function), one must use an EXTERNAL statement again, this time listing the function arguments which have been passed if one wishes to pass these same arguments again (and they cannot otherwise be identified as subprograms).

For example, supposed F and SUB1 are intended to be dummy names for subprograms to be passed into SUB2.

	SUBROUTINE SUB2(A,B,F,SUB1)
	EXTERNAL F
	REAL A,B
	CALL SUB1(F,A,B)
	RETURN
	END
Since SUB1 was used in the CALL statement, the compiler recognizes it as a subroutine and it need not be listed in the EXTERNAL statement. Since, however, there is no indication that F is a subprogram (by default typing, it is a real variable), it must be listed in the EXTERNAL statement.

NOTE: In Fortran-90/95/2003 (and FORTRAN-77), one cannot pass as arguments generic function names, numeric type conversion functions, lexical comparison functions, or maximum and minimum functions. If one wishes to make use of certain intrinsic functions as arguments, one must use the appropriate "specific" name corresponding to the data type for which it will be used within the program segment. In most cases, general function names are equivalent to the specific name typed according to the default typing. In other words, since ABS(x) (a generic function name) is by default of type real (and not integer), if it is used as an argument, it will be assumed to be of type real rather than be a generic type that might conform to integer, double precision, or complex arguments. If one wished to use the corresponding function for integer or complex arguments, one must use the "specific" function name (see a complete Fortran-90/95/2003 manual).

10.10 Multiple Entry Points

FORTRAN-77 (and Fortran-90/95/2003) permits the combining of several similar subroutines or functions into one larger subprogram, yet retain independent entry points into the subprogram code by using the ENTRY subheader statement. The ENTRY statement is similar to a function or subroutine in that the entry point is given a name and an argument list. Thus, for example,
	ENTRY NEWSRT(A,B)

The entry name (e.g., NEWSRT) for a function should be declared as to its correct type after the header statement of the subprogram, along with any arguments in the argument list.

The ENTRY statement provides an alternate place to start the action of the subprogram. This can be useful especially if two or more subprograms share a significant amount of common code. Since multiple entry points, in effect, create different subprograms, some authors discourage using ENTRY and recommend using a MODULE or creating another subprogram which contains the common code and is called by other subprograms.

10.11 SAVE Statement

Normally when a subprogram is completed, the values of all variables are left undefined. If one wishes to re-use a function or subroutine several times and also wishes the previous local variables to retain old values, this can be done by means of invoking the SAVE specification statement.

The SAVE statement comes before any DATA (see section 18.5) or executable statement and lists those variables whose values are to be saved. The name of a COMMON block (see section 18.6) may also be listed. If no variables are listed, all variable values are saved. As an example, if one wishes to save the values of variables A, B, and C, the following statement is used:

	SAVE A, B, C

This page is maintained by Dennis C. Smolarski, S.J. dsmolarski@math.scu.edu
© Copyright ©right; 1998-2005 Dennis C. Smolarski, SJ, All rights reserved.
Last changed: 23 June 2005.