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:

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,

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,

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 put a space between the type declaration and the asterisk, 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 be readable.

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) reminds us that these are pointers to such values. Alternatively, some programmers include "ptr" in the name. For example, pUsableArea or p_final_balance or AreaPtr. Names like these reinforce how they are to be used within the program (i.e., with asterisks for dereferencing when values are desired). Pick a style that works for you and then be consistent in your naming style.