Creating
functions
In
old (pre-Standard) C, you could call a function with any number or type of
arguments, and the compiler wouldn’t complain. Everything seemed fine
until you ran the program. You got mysterious results (or worse, the program
crashed) with no hints as to why. The lack of help with argument passing and
the enigmatic bugs that resulted is probably one reason why C was dubbed a
“high-level assembly language.”
Pre-Standard C programmers just adapted to it.
Standard
C and C++ use a feature called
function
prototyping
.
With
function prototyping, you must use a description of the types of arguments when
declaring and defining a function. This description is the
“prototype.” When the function is called, the compiler uses the
prototype to ensure the proper arguments are passed in, and that the return
value is treated correctly. If the programmer makes a mistake when calling the
function, the compiler catches the mistake.
Essentially,
you learned about function prototyping (without naming it as such) in the
previous chapter, since the form of function declaration in C++ requires proper
prototyping. In a function prototype, the argument list contains the types of
arguments that must be passed to the function and (optionally for the
declaration) identifiers for the arguments. The order and type of the arguments
must match in the declaration, definition and function call. Here’s an
example of a function prototype in a declaration:
int
translate(float x, float y, float z);
You
do not use the same form when declaring variables in function prototypes as you
do in ordinary variable definitions. That is, you cannot say:
float
x, y, z
.
You must indicate the type of
each
argument. In a function declaration, the following form is also acceptable:
int
translate(float, float, float);
Since
the compiler doesn’t do anything but check for types when the function is
called, the identifiers are only included for clarity, when someone is reading
the code.
In
the function definition, names are required because the arguments are
referenced inside the function:
int translate(float x, float y, float z) {
x = y = z;
// ...
}
It
turns out this rule only applies to C. In C++, an argument may be unnamed in
the argument list of the function definition. Since it is unnamed, you cannot
use it in the function body, of course. The reason unnamed arguments are
allowed is to give the programmer a way to “reserve space in the argument
list.” Whoever uses the function must still call the function with the
proper arguments. However, the person creating the function can then use the
argument in the future without forcing modification of code that calls the
function. This option of ignoring an argument in the list is also possible if
you leave the name in, but you will get an obnoxious warning message about the
value being unused every time you compile the function. The warning is
eliminated if you remove the name.
C
and C++ have two other ways to declare an argument list. If you have an empty
argument list you can declare it as
func( )
in C++, which tells the compiler there are exactly zero arguments. You should
be aware that this only means an empty argument list in C++. In C it means
“an indeterminate number of arguments (which is a “hole” in C
since it disables type checking in that case). In both C and C++, the
declaration
func(void);
means an empty argument list. The
void
keyword means “nothing” in this case (it can also mean “no
type” in the case of pointers, as you’ll see later in this chapter). The
other option for argument lists occurs when you don’t know how many
arguments or what type of arguments you will have; this is called a variable
argument list.
This “uncertain argument list” is represented by ellipses (
...).
Defining a function with a variable argument list is significantly more
complicated than defining a regular function. You can use a variable argument
list for a function that has a fixed set of arguments if (for some reason) you
want to disable the error checks of function prototyping. Because of this, you
should restrict your use of variable argument lists to C, and avoid them in C++
(where, as you’ll learn, there are much better alternatives). Handling
variable argument lists is described in the library section of your local C
guide.
Function
return values
A
C++ function prototype must specify the return value type of the function (in
C, if you leave off the return value type it defaults to
int).
The return type specification precedes the function name. To specify that no
value is returned, use the
void
keyword.
This will generate an error if you try to return a value from the function.
Here are some complete function prototypes:
int f1(void); // Returns an int, takes no arguments
int f2(); // Like f1() in C++ but not in Standard C!
float f3(float, int, char, double); // Returns a float
void f4(void); // Takes no arguments, returns nothing
To
return a value from a function, you use the
return
statement.
return
exits the function, back to the point right after the function call. If
return
has an argument, that argument becomes the return value of the function. If a
function says that it will return a particular type, then each
return
statement
must return that type. You can have more than one
return
statement in a function definition:
//: C03:Return.cpp
// Use of "return"
#include <iostream>
using namespace std;
char cfunc(const int i) {
if(i == 0)
return 'a';
if(i == 1)
return 'g';
if(i == 5)
return 'z';
return 'c';
}
int main() {
cout << "type an integer: ";
int val;
cin >> val;
cout << cfunc(val) << endl;
} ///:~
In
cfunc( ),
the first
if
that evaluates to
true
exits the function via the
return
statement. Notice that a function declaration is not necessary because the
function definition appears before it is used in
main( ),
so the compiler knows about it from that function definition.
Using
the C function library
All
the functions in your local C function library are available while you are
programming in C++. You should look hard at the function library before
defining your own function – there’s a good chance that someone has
already solved your problem for you, and probably given it a lot more thought
and debugging.
A
word of caution, though: many compilers include a lot of extra functions that
make life even easier and are very tempting to use, but are not part of the
Standard C library. If you are certain you will never want to move the
application to another platform (and who is certain of that?), go ahead
–use those functions and make your life easier. If you want your
application to be portable, you should restrict yourself to Standard library
functions. If you must perform platform-specific activities, try to isolate
that code in one spot so it can easily be changed when porting to another
platform. In C++, platform-specific activities are often encapsulated in a
class, which is the ideal solution.
The
formula for using a library function is as follows: first, find the function in
your programming reference (many programming references will index the function
by category as well as alphabetically). The description of the function should
include a section that demonstrates the syntax of the code. The top of this
section usually has at least one
#include
line, showing you the header file containing the function prototype. Duplicate
this
#include
line in your file, so the function is properly declared.
Now you can call the function in the same way it appears in the syntax section.
If you make a mistake, the compiler will discover it by comparing your function
call to the function prototype in the header, and tell you about your error.
The linker searches the standard library by default, so that’s all you
need to do: include the header file, and call the function.
Creating
your own libraries with the librarian
You
can collect your own functions together into a library. Most programming
packages come with a librarian that manages groups of object modules. Each
librarian has its own commands, but the general idea is this: if you want to
create a library, make a header file containing the function prototypes for all
the functions in your library. Put this header file somewhere in the
preprocessor’s search path, either in the local directory (so it can be
found by
#include
"header"
)
or in the include directory (so it can be found by
#include
<header>
).
Now take all the object modules and hand them to the librarian along with a
name for the finished library (most librarians require a common extension, such
as
.lib
or
.a).
Place the finished library where the other libraries reside, so the linker can
find it. When you use your library, you will have to add something to the
command line so the linker knows
to search the library for the functions you call. You must find all the details
in your local manual, since they vary from system to system.
Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru