When Fortran was originally developed memory was at a premium. Variables and procedure names could have a maximum of 6 characters, and variables were often implicitly typed. This means that the first letter of the variable name determines its type.
Programs like the following are acceptable Fortran:
program badbadnotgood j = 4 key = 5 ! only the first letter determines the type x = 3.142 print*, "j = ", j, "key = ", key, "x = ", x end program badbadnotgood
You may even define your own implicit rules with the
! all variables are real by default implicit real (a-z)
! variables starting with x, y, z are complex ! variables starting with c, s are character with length of 4 bytes ! and all other letters have their default implicit type implicit complex (x,y,z), character*4 (c,s)
Implicit typing is no longer considered best practice. It is very easy to make a mistake using implicit typing, as typos can go unnoticed, e.g.
program oops real :: somelongandcomplicatedname ... call expensive_subroutine(somelongandcomplEcatedname) end program oops
This program will happily run and do the wrong thing.
To turn off implicit typing, the
implicit none statement can be used.
program much_better implicit none integer :: j = 4 real :: x = 3.142 print*, "j = ", j, "x = ", x end program much_better
If we had used
implicit none in the program
oops above, the compiler would have noticed immediately, and produced an error.
if statement allows one to use three branches depending on the result of an arithmetic expression
if (arith_expr) label1, label2, label3
if statement transfers control flow to one of the labels in a code. If the result of
arith_expr is negative
label1 is involved, if the result is zero
label2 is used, and if the result is positive last
label3 is applied. Arithmetic
if requires all three labels but it allows the re-use of labels, therefore this statement can be simplified to a two branch
if (N * N - N / 2) 130, 140, 130 if (X) 100, 110, 120
Now this feature is obsolete with the same functionality being offered by the
if statement and
if-else construct. For example, the fragment
if (X) 100, 110, 120 100 print*, "Negative" goto 200 110 print*, "Zero" goto 200 120 print*, "Positive" 200 continue
may be written as the
if (X<0) then print*, "Negative" else if (X==0) then print*, "Zero" else print*, "Positive" end if
if statement replacement for
if (X) 100, 100, 200 100 print *, "Negative or zero" 200 continue
if (X<=0) print*, "Negative or zero"
do construct looks like
integer i do 100, i=1, 5 100 print *, i
That is, where the labelled termination statement is not a
continue statement. There are various restrictions on the statement that can be used as the termination statement and the whole thing is generally very confusing.
Such a non-block construct can be rewritten in block form as
integer i do 100 i=1,5 print *, i 100 continue
or better, using an
end do termination statement,
integer i do i=1,5 print *, i end do
Alternate return is a facility to control the flow of execution on return from a subroutine. It is often used as a form of error handling:
real x call sub(x, 1, *100, *200) print*, "Success:", x stop 100 print*, "Negative input value" stop 200 print*, "Input value too large" stop end subroutine sub(x, i, *, *) real, intent(out) :: x integer, intent(in) :: i if (i<0) return 1 if (i>10) return 2 x = i end subroutine
The alternate return is marked by the arguments
* in the subroutine dummy argument list.
call statement above
*200 refer to the statements labelled
In the subroutine itself the
return statements corresponding to alternate return have a number. This number is not a return value, but denotes the provided label to which execution is passed on return. In this case,
return 1 passes execution to the statement labelled
return 2 passes execution to the statement labelled
200. An unadorned
return statement, or completion of subroutine execution without a
return statement, passess execution to immediately after the call statement.
The alternate return syntax is very different from other forms of argument association and the facility introduces flow control contrary to modern tastes. More pleasing flow control can be managed with return of an integer "status" code.
real x integer status call sub(x, 1, status) select case (status) case (0) print*, "Success:", x case (1) print*, "Negative input value" case (2) print*, "Input value too large" end select end subroutine sub(x, i, status) real, intent(out) :: x integer, intent(in) :: i integer, intent(out) :: status status = 0 if (i<0) then status = 1 else if (i>10) status = 2 else x = i end if end subroutine
Fortran originally was designed for a fixed format form based on an 80 column punched card:
Yes: This is a line of the author's own code
These were created on a card punch machine, much like this:
Images are original photography by the author
The format, as shown on the illustrated sample card, had the first five columns reserved for statement labels. The first column was used to denote comments by a letter C. The sixth column was used to denote a statement continuation (by inserting any character other than a zero '0'). The last 8 columns were used for card identification and sequencing, which was pretty valuable if you dropped your deck of cards on the floor! The character coding for punched cards had only a limited set of characters and was upper case only. As a result, Fortran programs looked like this:
DIMENSION A(10) 00000001 C THIS IS A COMMENT STATEMENT TO EXPLAIN THIS EXAMPLE PROGRAM 00000002 WRITE (6,100) 00000003 100 FORMAT(169HTHIS IS A RATHER LONG STRING BEING OUTPUT WHICH GOES OVE00000004 1R MORE THAN ONE LINE, AND USES THE STATEMENT CONTINUATION MARKER IN00000005 2COLUMN 6, AND ALSO USES HOLLERITH STRING FORMAT) 00000006 STOP 00000007 END 00000008
The space character was also ignored everywhere, except inside a Hollerith character constant (as shown above). This meant that spaces could occur inside reserved words and constants, or completely missed out. This had the side effect of some rather misleading statements such as:
DO 1 I = 1.0
is an assignment to the variable
DO1I = 1,0
is actually a
DO loop on the variable
Modern Fortran does not now required this fixed form of input and permits free form using any columns. Comments are now indicated by a
! which can also be appended to a statement line. Spaces are now not permitted anywhere and must be used as separators, much as in most other languages. The above program could be written in modern Fortran as:
! This is a comment statement to explain this example program Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH no longer GOES OVER MORE THAN ONE LINE, AND does not need to USE THE STATEMENT CONTINUATION MARKER IN COLUMN 6, or the HOLLERITH STRING FORMAT"
Although the old-style continuation is no longer used, the above example illustrates that very long statements will still occur. Modern Fortran uses a
& symbol at the end and beginning of the continuation. For example, we could write the above in a more readable form:
! This is a comment statement to explain this example program Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH still & &GOES OVER MORE THAN ONE LINE, AND does need to USE THE STATEMENT & &CONTINUATION notation"
In the early forms of Fortran the only mechanism for creating global variable store visible from subroutines and functions is to use the
COMMON block mechanism. This permitted sequences of variables to be names and shared in common.
In addition to named common blocks there may also be a blank (unnamed) common block.
A blank common block could be declared like
common i, j
whereas the named block
variables could be declared like
common /variables/ i, j
As a complete example, we could imagine a heap store that is used by routines that can add and remove values:
PROGRAM STACKING COMMON /HEAP/ ICOUNT, ISTACK(1023) ICOUNT = 0 READ *, IVAL CALL PUSH(IVAL) CALL POP(IVAL) END SUBROUTINE PUSH(IVAL) COMMON /HEAP/ ICOUNT, ISTACK(1023) ICOUNT = ICOUNT + 1 ISTACK(ICOUNT) = IVAL RETURN END SUBROUTINE POP(IVAL) COMMON /HEAP/ ICOUNT, ISTACK(1023) IVAL = ISTACK(ICOUNT) ICOUNT = ICOUNT - 1 RETURN END
Common statements may be used to implicitly declare the type of a variable and to specify the
dimension attribute. This behaviour alone is often a sufficient source of confusion. Further, the implied storage association and requirements for repeated definitions across program units makes the use of common blocks prone to error.
Finally, common blocks are very restricted in the objects they contain. For example, an array in a common block must be of explicit size; allocatable objects may not occur; derived types must not have default initialization.
In modern Fortran this sharing of variables can be handled by the use of modules. The above example can be written as:
module heap implicit none ! In Fortran 2008 all module variables are implicitly saved integer, save :: count = 0 integer, save :: stack(1023) end module heap program stacking implicit none integer val read *, val call push(val) call pop(val) contains subroutine push(val) use heap, only : count, stack integer val count = count + 1 stack(count) = val end subroutine push subroutine pop(val) use heap, only : count, stack integer val val = stack(count) count = count - 1 end subroutine pop end program stacking
Named and blank common blocks have slightly different behaviours. Of note:
saveattribute; objects in named common blocks without the
saveattribute may become undefined when the block is not in the scope of an active program unit
This latter point can be contrasted with the behaviour of module variables in modern code. All module variables in Fortran 2008 are implicitly saved and do not become undefined when the module goes out of scope. Before Fortran 2008 module variables, like variables in named common blocks, would also become undefined when the module went out of scope.
Assigned GOTO uses integer variable to which a statement label is assigned using the ASSIGN statement.
100 CONTINUE ... ASSIGN 100 TO ILABEL ... GOTO ILABEL
Assigned GOTO is obsolescent in Fortran 90 and deleted in Fortran 95 and later. It can be avoided in modern code by using procedures, internal procedures, procedure pointers and other features.
Computed GOTO allows branching of the program according to the value of an integer expression.
GOTO (label_1, label_2,... label_n) scalar-integer-expression
scalar-integer-expression is equal to 1 the program continues at statement label
label_1, if it is equal to 2 it goes to
label_2 and so on. If it is less then
1 or larger than
n program continues on next line.
ivar = 2 ... GOTO (10, 20, 30, 40) ivar
will jump to statement label 20.
This form of
goto is obsolescent in Fortran 95 and later, being superseded by the
select case construct.
Before Fortran 95 it was possible to use assigned formats for input or output. Consider
integer i, fmt read *, i assign 100 to fmt if (i<100000) assign 200 to fmt print fmt, i 100 format ("This is a big number", I10) 200 format ("This is a small number", I6) end
assign statement assigns a statement label to an integer variable. This integer variable is later used as the format specifier in the
Such format specifier assignment was deleted in Fortran 95. Instead, more modern code can use some other form of execution flow control
integer i read *, i if (i<100000) then print 100, i else print 200, i end if 100 format ("This is a big number", I10) 200 format ("This is a small number", I6) end
or a character variable may be used as the format specifier
character(29), target :: big_fmt='("This is a big number", I10)' character(30), target :: small_fmt='("This is a small number", I6)' character(:), pointer :: fmt integer i read *, i fmt=>big_fmt if (i<100000) fmt=>small_fmt print fmt, i end
Consider the program
implicit none integer f, i f(i)=i print *, f(1) end
f is a statement function. It has integer result type, taking one integer dummy argument.1
Such a statement function exists within the scope in which it is defined. In particular, it has access to variables and named constants accessible in that scope.
However, statement functions are subject to many restrictions and are potentially confusing (looking at casual glance like an array element assignment statement). Important restrictions are:
The main benefits of statement functions are repeated by internal functions
implicit none print *, f(1) contains integer function f(i) integer i f = i end function end
Internal functions are not subject to the restrictions mentioned above, although it is perhaps worth noting that an internal subprogram may not contain further internal subprogram (but it may contain a statement function).
Internal functions have their own scope but also have available host association.
1 In real old code examples, it wouldn't be unusual to see the dummy arguments of a statement function being implicitly typed, even if the result has explicit type.