Functions and Pointer Parameters
Introduction
Today we reveal an important aspect of memory and parameter passing that has been mostly abstracted in your programming work so far. Namely, we show how to identify the memory address of a particular variable, which can be used as a function parameter to modify the variable's value outside that procedure.
Textbook Reading
Begin with a reading from your textbook:
- King: Sections 11.1-11.5, pp. 241-255
Summary of Value and Address Parameters
The lab on functions and parameters considered two basic types of function parameters:
Suppose variable number is declared as follows in
a main function:
double number = 123.45;
Value Parameter Passage
In the following code, execution of the valueAsParameter
function creates a new variable valueParameter, and the call
of the valueAsParameter copies a value
to valueParameter.
As an example, consider the following code:
void
valueAsParameter (double valueParameter)
{
printf ("value of valueParameter at start of valueAsParameter: %lf\n",
valueParameter);
valueParameter = 543.21;
printf ("value of valueParameter at end of valueAsParameter: %lf\n",
valueParameter);
} //valueAsParameter
int
main (void)
{
double number = 123.45;
valueAsParameter (number);
printf ("value of number after valueAsParameter completed: %lf\n", number);
} // main
The stack model of computation reminds us that when this code is executed,
numbergets an initial value of 123.45- when
valueAsParameteris called,- new storage is allocated for
valueParameter - 123.45 is copied into
valueParameter - the value of
valueParameter(123.45) is printed - the value stored in
valueParameteris changed to 543.21 (but the value stored innumberis unaffected) - the new value 543.21 of
valueParameteris printed - when
valueAsParameteris done,valueParameteris deallocated, and the value 543.21 is lost
- new storage is allocated for
-
numberremains 123.45, and this number is printed inmain
Altogether, value parameter passage copies a value to the new
parameter, work in the function works with the copied value, and
changes to the new parameter do not affect the original variable
(in main).
Passing Addresses as Parameters
In the following code, execution of
the addressAsParameter function stores the address (not
the value) of the original variable. Using the address as the
parameter, changes at the stored address refer back to
the main variable.
As an example, consider the following code:
void
addressAsParameter (double * addressParameter)
{
printf ("value of valueParameter at start of addressAsParameter: %lf\n",
*addressParameter);
*addressParameter = 543.21;
printf ("value of valueParameter at end of addressAsParameter: %lf\n",
*addressParameter);
} // addressAsParameter
int
main (void)
{
double number = 123.45;
addressAsParameter (&number);
printf ("value of number after addressAsParameter completed: %lf\n",
number);
} // main
When this code is executed,
numbergets an initial value of 123.45- when
addressAsParameteris called,addressParameteris given the address of variablenumber- the value in the location referenced by
addressParameter(123.45) is printed - the value at the location referenced by
addressParameteris changed to 543.21 (i.e., the value stored innumberis changed) - the new value 543.21 referenced by
addressParameteris printed - when
addressAsParameteris done,addressParameteris deallocated, but the changed value innumberremains
-
numbercontains 543.21, and this number is printed inmain
Notes on Style
Pointers are not excessively difficult to master, but following a few stylistic guidelines will certainly help avoid some common errors and confusion.
First, when defining pointer variables, treat the asterisk
(*) as part of the variable name, rather than the
type. That is, because C requires each pointer variable to have its own
asterisk, you should any space between the type declaration, rather
than the asterisk and the name.
POOR: int* p, q;
BETTER: int *p, q;
The first example is stylistically poor because a careless programmer
might either miss that variable p is in fact a pointer to
an integer (though compiler warnings should nearly always fix this), or
that variable q is NOT a pointer to an integer. The
compiler knows the difference, but you should still write your code to
reflect it readably.
If both p and q were to be pointers, you'd
need to write the following:
int *p, *q;
The BEST strategy is to avoid the potential for confusion altogether by declaring pointer variables and non-pointer variables separately:
int * p;
int q;
The extra space ensures the asterisk is clearly visible and does not get visually "glossed" by reading quickly and associating it with either the type or the variable name. This issue brings us to a second stylistic injunction.
Naming conventions help clarify the role of variables in your
program text. Whereas variables should already have clear, noun-based
names, like area or balance, appending a
prefix p (when using "camel case") or p_
(when using underscores) qualifies them as pointers to such
values. For example, pUsableArea
or p_final_balance. Names like these reinforce how they
are to be used within the program (i.e., with asterisks for
dereferencing when values are desired).
