Functions
Functions are the cornerstones of large C programs.
We use them for
- invoking class methods,
- peforming I/O operations
C has no separate input or output statements -
unlike Pascal, BASIC or FORTRAN - and all input and output is performed by
invoking functions. You can view these functions as methods on the I/O devices, but
C textbooks simply label them as I/O functions
- enabling repetitive operations to be coded once and invoked
many times and
- enabling you to think and program at a much higher level of abstraction (ie
you write functions to do all the low level operations so that you can subsequently forget about them!
Anatomy of a function
There are two parts to any function:
| Specification |
- gives the function its name
- specifies what values it returns
- lists its arguments
|
double area( Rectangle r );
/* Calculate the area of rectangle r */
|
| Implementation |
- the actual code of the function
- statements which perform the function's actions
|
double area( Rectangle r ) {
return r->height * r->width;
}
|
Note that this is just the same as the basic structure of a class:
where the specification consists primarily of the name and
the specifications of the methods (which are, of course, just functions!).
Specification
The specification simply defines the function's name, its argument list and the type of the value that it returns (or specifies that no value is returned).
It is the interface that the function provides for a programmer who wants to invoke it. A programmer using the function only needs to know the specification and, of course, what the function is supposed to do. He or she can forget about the details of how the function performs its operation. This enables us to think at a higher level - by forgetting about small details.
It also enables us to build more reliable programs by carefully checking and verifying the operation of each function separately - and then using it in large programs with a high degree of confidence that it is correct.
Here are two ways of formally describing the syntax of a function specification or prototype:
or
function_prototype ::= [ static ] type_specifier name ( formal_parameter_list );
type_specifier ::= void | name
name ::= legal_C_name
formal_parameter_list ::= null | parameter_list
parameter_list ::= type_specifier name [, parameter list ]
In the first form, often called a railroad diagram, you follow the arrows around the diagram: when you encounter a box, you put down the object defined in the box and continue until you reach the right hand side. Any path which follows the arrows and includes the required elements is a legal function specification which should be accepted by any ANSI C compiler.
The second form, called Backus-Naur Form or Backus Normal Form was named after two influential computer scientists,
John Backus and Peter Naur.
It consists of a series of meta-symbols (all the symbols in italic script are meta-symbols) and their definitions.
A definition of a syntatic construct can include meta-symbols:
when this occurs, you look for that symbol's definition
to work out what can legally be substituted at that point.
Square brackets ([]) enclose optional items
(which can be left out - eg the keyword static is optional
at the beginning of a function prototype.
Both represenations of the formal syntax of a function prototype
are the same!
Follow some paths through the railroad diagrams and match them
with the BNF description to confirm this.
C names
Since we've just formally introduced it above, let's look at what
constitutes a legal name in a C program.
In the C standard, a name is called an identifier.
Names are used for
- functions,
- classes or types,
- objects or variables,
- constants
Informally, a name starts with a letter or an underscore (_) and
is followed by any number of letters, underscores and digits.
It's railroad diagram definition is:
and the equivalent BNF is:
legal_C_name ::=
identifier ::= { letter | underscore
{ letter | digit | underscore }*
where the braces indicate choose one
of the alternatives which they enclose
and braces followed by a * indicate any number of repetitions
of the pattern inside (equivalent to a loop in the railroad diagram).
Implementation
The implementation of a function contains the statements that the
computer will execute when the function is invoked.
Formally it is composed of a header part,
which is identical to the specification followed by
a block.
or
function ::= [ static ] type_specifier name ( formal_parameter_list ) block
block ::= { dec_list ;
statements }
dec_list ::= null | { type_specifier name_list
}*
statements ::= null | { statement ; }*
© John Morris, 1997