FORTRAN LESSON 6

Lesson Topics
Initializing VariablesCall Statement
ModReturn in Subroutines
SubroutinesVariable Substitution
View DemosDownload Demos
# 1 # 2 # 3 # 4 # 1 # 2 # 3 # 4
Main Fortran Page

Initializing Variables

Recall that in Basic the default value of a numeric variable is always zero - that is, if you introduce a numeric variable but do not specify its value, Basic automatically gives it the value zero. In GNU Fortran the situation is more confused. A real variable with no value specified will be given a value - but usually a very small value that is not precisely zero, and sometimes a value that is not even close to zero. An integer is given the default value 1. This strange behavior is hardly ever a problem, as usually when the variable is eventually used in the program it is given an appropriate value by some assignment statement. But trouble might arise if a forgetful programmer proceeds on the assumption that the default value is zero, or perhaps neglects to include an assignment statement. If you are worried about the problem, you can assign values to all your variables at the beginning of your program - a procedure called "initializing variables". The easiest way to do this is with ordinary assignment statements, such as "x = 0", or "y = 2.61", etc. (For programs with a large number of variables a more efficient method is to use DATA statements; we will discuss these later.)

Mod

In Fortran the expression mod(n,m) gives the remainder when n is divided by m; it is meant to be applied mainly to integers. Examples are

mod(8,3) = 2   ,   mod(27,4) = 3   ,   mod(11,2) = 1   ,   mod(20,5) = 0  .

Subroutines

A subroutine in Fortran works like a subprogram in Basic, except that you do not declare a subroutine. Subroutines are typed in the source file after the main program. A subroutine must have a name, followed by a list of variables in parentheses. A variable may be of any type, including a character variable, and can be an array. A subroutine begins with variable declaration statements, just as the main program.

The main program uses a call statement to call the subroutine. The call statement has also a list of variables, which are substituted for the subroutine variables. The subroutine executes, modifying some or all of its variables, which are then substituted back for the original call variables in the main program. The variables in the call statement must match the variables in the subroutine according to number, type, and dimension. (Oversights lead to type-mismatch error messages by the compiler.)

Here is a simple program named average that prompts the user for two real numbers, calls a subroutine named avg to average the numbers, and then prints the average.

program average
real x, y, z
print *, "What are the two numbers you want to average?"
read *, x, y
call avg(x,y,z)
print *, "The average is", z
end
 
subroutine avg(a,b,c)
real a, b, c
c = (a + b)/2.
end

When the subroutine is called it substitutes x for a, y for b, and z for c. (Although the user does not input z, GNU Fortran will have given it some default value.) After the subroutine does its calculations, the new values of a, b, c are substituted back into the main program for x, y, z. (In this particular subroutine only c changes, so x and y retain their original values.) After the subroutine completes its run, action is returned to the statement in the main program immediately following the call statement.

Just remember that, except for the first statement naming the subroutine and listing the variables, a subroutine has the same general structure as a main program. It begins with type and dimension statements, has a main body carrying out the action, and concludes with an end statement.

The advantage of using subroutines is that the main program can be kept relatively simple and easy to follow, while nitty-gritty calculations and complex procedures are shuffled off to various subroutines, each performing a specific task. A well-written subroutine can be saved in a subroutine "library", to be inserted into other main programs as the need arises.

A subroutine can call another subroutine, and it can also access a function subprogram.

A subroutine need not depend on any variables - in which case no parentheses follow the subroutine name. Here is a simple subroutine involving no variables:

subroutine bluesky
print *, "The sky is blue."
end

The call statement for this subroutine,

call bluesky     ,

likewise lists no variables.

The following subroutine computes the product of a 2 x 2 matrix A with a 2 x 1 vector x, according to the formula

It accepts as variables a 2 x 2 array A and one-dimensional arrays x and y, each indexed from 1 to 2. The array y represents the product y = Ax.

subroutine prod(A,x,y)
real A(2,2), x(2), y(2)
y(1) = A(1,1) * x(1) + A(1,2) * x(2)
y(2) = A(2,1) * x(1) + A(2,2) * x(2)
end

A call statement for this subroutine might be something like

call prod(B,u,v)    ,

where B and u are arrays known to the main program and the product v is to be computed by the subroutine. Of course the main program will have appropriately dimensioned these arrays. After the subroutine completes its task and returns control to the main program, the array v will represent the product Bu.

Return (in Subroutines)

A return statement in a subroutine instructs Fortran to terminate the subroutine and return to the main program at the point where it departed. Thus it works like a stop statement in the main program, halting the program prematurely before the final end statement. A subroutine may have several returns, but only one end statement.

Here is a subroutine, using a return statement, that decides whether a positive integer n is a prime number:

subroutine check(n,result)
integer n, i, root
character result*9
if (n .eq. 1) then
result = "not prime"
return
end if
root = sqrt(real(n))
do i = 2, root
if (mod(n,i) .eq. 0) then
result = "not prime"
return
end if
end do
result = "prime"
end

The subroutine begins by checking whether n = 1, and if true it sets result = "not prime" and returns to the main program. If n > 1 the DO LOOP looks at integers from 2 up to the square root of n, checking whether each is a divisor of n. If and when it finds such a divisor, it sets result = "not prime" and returns to the main program. But if no divisor of n is found, the subroutine completes the entire loop and sets result = "prime". After the subroutine ends, the main program need only look at the value of result to find out whether n is prime or not prime.

Variable Substitution in Subprograms

We look in more detail at how variables are substituted for one another in the calling and execution of a subroutine or function subprogram. Let us suppose for example that a certain subroutine named "demo" depends on three variables, say a, b, and c, so that the first line of the subroutine is

subroutine demo(a,b,c)     .

Let us assume also that the main program's call statement for this subroutine is

call demo(x,y,z)     ,

where x, y, and z are variables from the main program. The types and dimensions of x, y, and z will have been declared in the main program, and these must match the types and dimensions of a, b, and c, respectively, as declared in the subroutine.

The values of x, y, and z will have been stored by Fortran in certain memory locations, designated in the diagram below as triangles:

x → Δ
y → Δ
z → Δ

When the subroutine "demo" is called, Fortran assigns the variable a the same memory location as x, b the same location as y, and c the same as z:

x → Δ ← a
y → Δ ← b
z → Δ ← c

(This explains why the types and dimensions must match!) Now, as the subroutine "demo" runs, the variables a, b and c might change to new values. But since x, y, and z share memory locations with a, b, and c, the values of x, y, and z of course will have to change simultaneously along with a, b, and c. When the subroutine terminates and returns control to the main program, a, b, and c then are no longer active variables, but x, y, and z retain the final values of a, b, and c at the conclusion of the subroutine.

There is a way to fool Fortran into not changing the value of a calling variable when the subroutine runs. In the above example, suppose we change the call statement to

call demo(x,(y),z)    .

When the variable y is enclosed in parentheses, Fortran treats (y) as a new expression and assigns it a different memory location than that of y, but with the same value as y. The last diagram changes to

x → Δ ← a
y → Δ
(y) → Δ ← b
z → Δ ← c

Now, as b changes values during the execution of the subroutine, y is unaffected, so that at the conclusion of the subroutine y has its original value.

The above analysis applies to function subprograms as well as to subroutines. Changes in the function variables during execution of a function subprogram induce corresponding changes in the variables used to call the function subprogram.