Functions
A
function is simply a series of statements that have been grouped together and
given a name. Although the term "function" comes from mathematics, C
functions don't always resemble math functions. In C, a function doesn't
necessarily have arguments, nor does it necessarily compute a value. (In some
programming languages, a "function" returns a value, whereas a
"procedure" doesn't. C lacks this distinction.)
Functions
are the building blocks of C programs. Each function is essentially a small
program, with its own declarations and statements. Using functions, we can
divide a program into small pieces that are easier for us—and others—to understand
and modify. Functions can take some of the tedium out of programming by
allowing us to avoid duplicating code that's used more than once. Moreover,
functions are reusable: we can take a function that was originally part of one
program and use it in others.
Defining
and Calling Functions
Before we
go over the formal rules for defining a function, let's look at three simple
programs that define functions.
Function
Definitions
let's look at the general form of a
function definition:
return-type function-name ( parameters ) {
declarations statements
}
The return type of a function is the type of value that the
function returns.
The
following rules govern the return type:
Functions
may not return arrays, but there are no other restrictions on the return type.
Specifying that the return type is
void indicates that the function doesn't return a value.
One way to avoid the problem of
call-before-definition is to arrange the program so that the definition of
each function precedes all its calls. Unfortunately, such an arrangement
doesn't always exist, and even when it does, it may make the program harder to
understand by putting its function definitions in an unnatural order.
Fortunately. C offers a better solution: declare each function before
calling it. A Junction declaration provides the compiler with a brief
glimpse at a function whose full definition will appear later. A function
declaration resembles the first line of a function definition with a semicolon
added at the end:
return-type function-name ( parameters ) ;
Needless
to say. the declaration of a function must be consistent with the function's
definition.
Arguments
Let's
review the difference between a parameter and an argument. Parameters appear
in function definitions; they're dummy names that represent values to be
supplied when the function is called. Arguments are expressions that
appear in function calls. When the distinction between argument
and parameter isn't important, I'll sometimes use argument to
mean either.
In C, arguments are passed by
value: when a function is called, each argument is evaluated and its value
assigned to the corresponding parameter. Since the parameter contains a copy of
the argument's value, any changes made to the parameter during the execution of
the function don't affect the argument. In effect, each parameter behaves like
a variable that's been initialized to the value of the matching argument.
The fact that arguments are passed
by value has both advantages and disadvantages. Since a parameter can be
modified without affecting the corresponding argument, we can use parameters as
variables within the function, thereby reducing the number of genuine
variables needed.
Argument
Conversions
C
allows function calls in which the types of the arguments don't match the types
of the parameters. The rules governing how the arguments are converted depend
on whether or not the compiler has seen a prototype for the function (or the
function's full definition) prior to the call:
The compiler has encountered a prototype prior to the call.
The value of each argument is implicitly converted to the type of the
corresponding parameter as if by assignment. For example, if an int argument
is passed to a function that was expecting a double, the argument is converted
to double automatically.
The
compiler has not encountered a prototype prior to the call. The compiler
performs the default argument promotions: (1) float arguments are convened to
double. (2) The integral promotions are performed, causing char and short arguments to be converted
to int.
The return Statement
A non-void function must use the return
statement to specify what value it will return. The return statement has the
form
return
expression ;
The expression is often just a constant
or variable:
return 0;
return status;
Program Termination
Since main is a function, it must have a return type. Normally, the
return type of main is int. which is why the programs we've seen so far have
defined main in the following way:
int main(void) {
}
Older
C programs often omit main's return type, taking advantage of the fact that it
traditionally defaults to int:
main() {
}
Omitting the return type of a
function isn't legal in C99, so it's best to avoid this practice. Omitting the
word void in main's parameter list remains legal, but—as a matter of style—it's
best to be explicit about the fact that main has no parameters. (We'll see
later that main sometimes does have two parameters, usually 13.7 named argc and argv.)
The value
returned by main is a status code that—in some operating sys- ^ terns—can be
tested when the program terminates, main should return 0 if the program
terminates normally; to indicate abnormal termination, main should return a
value other than 0. (Actually, there's no rule to prevent us from using the
return value for other purposes.) It's good practice to make sure that every C
program returns a status code, even if there are no plans to use it, since
someone running the program later may decide to test it.
The exit Function
Executing a return statement in main is one way to terminate
a program. Another is calling the exitfunction,which belongs to .
The argument passed to exit has the same meaning as main's
returnvalue:bothindicate the program's status at termination.
To indicate normal termination, we'd pass 0:
exit(0);/*
normal termination */
Since 0 is a bit cryptic, C allows
us to pass EXIT_SUCCESS instead (the
effect is the same):
exit(EXITSUCCESS); /* normal termination */
Passing EXIT_FAILURE indicates abnormal termination:
exit(EXIT_FAILURE);
/* abnormal termination */
EXIT_SUCCESS
and EXIT_FAILURE are macros defined
in . The values of
EXIT_SUCCESS and EXIT_FAILURE
are implementation- defined; typical values are 0 and I, respectively.As
methods of terminating a program, return and exit are closely related. In fact,
the statement
return expression;
in main is
equivalent to
exit (expression) ;
The difference between return and exit is that exit causes
program termination regardless of which function calls it. The return
statement causes program termination only when it appears in the main function.
Some programmers use exit exclusively to make it easier to locate all exit
points in a program.
Local
Variables
A
variable declared in the body of a function is said to be local to the
function. In the following function, sum is a local variable. By
default, local variables have the following properties:
Automatic storage duration. The storage duration (or extent)
of a variable is the portion of program execution during which storage for the
variable exists. Storage for a local variable is "automatically"
allocated when the enclosing function is called and deallocated when the
function returns, so the variable is said to have automatic storage duration. A
local variable doesn't retain its value when its enclosing function returns.
When the function is called again, there's no guarantee that the variable will
still have its old value.
Block scope. The scope of a variable is the portion of the
program text in which the variable can be referenced. A local variable has
block scope: it is visible from its point of declaration to the end of the
enclosing function body. Since the scope of a local variable doesn't extend
beyond the function to which it belongs, other functions can use the same name
for other purposes.
Static Local Variables
Putting the word static in the declaration of a local
variable causes it to have static storage duration instead of
automatic storage duration. A variable with static storage duration has a
permanent storage location, so it retains its value throughout the execution of
the program. Consider the following function:
void
f(void) {
static int
i; /* static local variable */
}
Since the local variable i has been declared static, it
occupies the same memory location throughout the execution of the program.
When f returns, i won't lose its value.
A
static local variable still has block scope, so it's not visible to other functions.
In a nutshell, a static variable is a place to hide data from other functions
but retain it for future calls of the same function.
External Variables
Passing arguments is one way to transmit information to a
function. Functions can also communicate through external variables—variables
that are declared outside the body of any function.
The
properties of external variables (or global variables, as they're
sometimes called) are different from those of local variables:
Static storage duration. External
variables have static storage duration, just like local variables that have
been declared static. A value stored in an external variable will stay there
indefinitely.
File scope. An external variable
has file scope: it is visible from its point of declaration to the end of the
enclosing file. As a result, an external variable can be accessed (and
potentially modified) by all functions that follow its declaration.
Example: Using External Variables to
Implement a Stack
To illustrate how external variables might be used, let's
look at a data structure known as a stack. (Slacks are an
abstract concept, not a C feature; they can be implemented in most programming
languages.) A stack, like an array, can store multiple data items of the same
type. However, the operations 011 a
stack are limited: we can either push an item onto the stack
(add it to one end—the "stack top") or pop it from the
stack (remove it from the same end). Examining or modifying an item that's not
at the top of the stack is forbidden.
One
way to implement a stack in C is to store its items in an array, which we'll
call contents. A separate integer variable named top marks the position of the
stack top. When Ihe stack is empty, top has the value 0. To push an item on the
stack, we simply store the item in contents at the position indicated by top,
then increment top. Popping an item requires decrementing top. then using it as
an index into contents to fetch the item that's being popped.
Pros and Cons of External Variables
External variables
are convenient when many functions must share a variable or when a few
functions share a large number of variables. In most cases, however, it's
better for functions to communicate through parameters rather than by sharing
variables.
If
an external variable is assigned an incorrect value, it may be difficult to
identify the guilty function. It's like trying to solve a murder committed at a
crowded party—there's no easy way to narrow the list of suspects.
Functions that rely on external variables are hard to reuse in other
programs. A function that depends on external variables isn't self-contained;
to reuse the function, we'll have to drag along any external variables that it
needs.
Many C
programmers rely far too much on external variables. One common abuse: using
the same external variable for different purposes in different functions.
Suppose that several functions need a variable named i to control a for
statement. Instead of declaring i in each function that uses it, some
programmers declare it at the top of the program, thereby making the variable
visible to all functions. This practice is poor not only for the reasons
listed earlier, but also because it's misleading; someone reading the program
later may think that the uses of the variable are related, when in fact they're
not.
When you
use external variables, make sure they have meaningful names. (Local variables
don't always need meaningful names: it's often hard to think of a better name
than i for the control variable in a for loop.) If you find yourself using
names like i and temp for external variables, that's a clue that perhaps they
should really be local variables.
Scope
In a C program, the same identifier may have several different
meanings. C's scope rules enable the programmer (and the compiler) to determine
which meaning is relevant at a given point in the program.
Here's
the most important scope rule: When a declaration inside a block names an
identifier that's already visible (because it has file scope or because it's
declared in an enclosing block), the new declaration temporarily "hides"
the old one, and the identifier takes on a new meaning. At the end of the
block, the identifier regains its old meaning.
No comments:
Post a Comment