Common Sense C - Advice and Warnings for C and C++ Programmers:Practical Pointers
Common Sense C - Advice & Warnings for C and C++ Programmers
by Paul Conte
29th Street Press
ISBN: 1882419006 Pub Date: 10/01/92
Previous
Table of Contents
Next
Chapter 6Practical Pointers
Deck: Learn some handy techniques for avoiding C pointer pitfallsby Paul Conte
If William Tell had used a C compiler for a bow and C pointers for arrows, hed have skewered Bill, Jr. not an apple to the tree. Although C pointers are simply memory addresses, theyre notorious for contributing to tricky programs and hard-to-spot program bugs. C pointers are easier to use than assembler address operands, but Cs low-level and unprotected exposure of addresses creates pitfalls for business application programs.
Perhaps the most common mistake when working with pointers is forgetting the * (the dereferencing or contents of) operator. The following code shows a fairly harmless but common example of this mistake:
int x = 25;
int *y;
y = &x;
printf( "y is %d\n", y );
What is printed is the address stored in y, not the value (i.e., 25) stored at that address. The correct printf statement is:
printf( "y is %d\n", *y );
Not all * omissions are so harmless and easy to detect. Consider the fees function in Figure 6.1. This function uses age and income to calculate registration and activity fees, which the fees function returns via two pointers passed by the caller.
Figure 6.1 Typical Function Returning Two Values
void function fees( int * rfee,
int * afee,
const int age,
const int income ) {
/*
| Calculate fees as base plus adjustment based on age
| and income
*/
* rfee = 100;
* afee = 50;
rfee += ( age >= 60 ) ? ( income < 50000 ? 0 : 50 ) :
( income < 50000 ? 100 : 200 );
afee += ( age >= 60 ) ? ( income < 50000 ? 10 : 20 ) :
( income < 50000 ? 30 : 40 );
}
The fees function will be compiled and executed without raising exception. But every call to fees will produce the same $100 registration fee and $50 activity fee, regardless of age or income. In this example, the third and fourth assignment statements increment the values of rfee and afee, which are addresses (pointers), not the integer values stored at these two addresses. The assignment statements targets should be *rfee and *afee. The compiler, however, cant tell the original version is wrong because addition operations are legal on both pointer and integer variables.
Cs lack of output parameters forces C programmers to explicitly handle addresses and dereferencing (i.e., referencing the storage pointed to by a pointer) to return more than one value from a function. Combined with Cs overloading of arithmetic operators for both integer and pointer arithmetic, dereferencing can easily trip you up. A good high-level language (HLL) should support output parameters so you dont need pointers and dereferencing to return multiple procedure values. (The C development community recognizes this C deficiency and has added references, which can be used for return parameters, to C++. But no such facility is planned for C itself.)
HLLs suitable for business programming also should either prohibit direct address modification (i.e., pointer arithmetic) or provide distinct functions for modifying addresses so such operations stand out in the code rather than appear as ordinary arithmetic operations. As Ive emphasized in previous chapters, C was designed as a portable assembly language, and when youre programming at the machine level, its logical to treat addresses as integers. At the business application level, however, machine addresses shouldnt be visible, much less easily confused with ordinary numbers.
You wont find a foolproof way to use dereferenced pointer parameters. If you try to code operands such as *rfee and *afee throughout a function, youll eventually slip up and omit the *. Finding the mistake may not be easy. But a simple coding practice will lead you around the pitfall: For non-array output or input/output parameters, use local variables instead of dereferenced parameters in function calculations.
Figure 6.2 shows the fees function rewritten to use two local variables in the calculations. The functions last two statements assign the calculated values to the locations pointed to by the pointer parameters. This technique isolates and simplifies dereferencing and can significantly reduce errors. Figure 6.3 shows how to handle in/out parameters by initializing the local variables to the dereferenced parameters.
Figure 6.2 Using Local Variables Instead of Dereferenced Parameters
void function fees( int * rfee,
int * afee,
const int age,
const int income ) {
/*
| Calculate fees as base plus adjustment based on age
| and income
*/
int reg_fee = 100;
int act_fee = 50;
reg_fee += ( age >= 60 ) ? ( income < 50000 ? 0 : 50 ) :
( income < 50000 ? 100 : 200 );
act_fee += ( age >= 60 ) ? ( income < 50000 ? 10 : 20 ) :
( income < 50000 ? 30 : 40 );
/*
| Return values
*/
* rfee = reg_fee;
* afee = act_fee;
}
Figure 6.3 Using Local Variables with In/Out Parameters
void function fees( int * rfee,
int * afee,
const int age,
const int income ) {
/*
| Adjust fees based on age and income
*/
int reg_fee = * rfee;
int act_fee = * afee;
reg_fee += ( age >= 60 ) ? ( income < 50000 ? 0 : 50 ):
( income < 50000 ? 100 : 200 );
act_fee += ( age >= 60 ) ? ( income < 50000 ? 10 : 20 ):
( income < 50000 ? 30 : 40 );
/*
| Return values
*/
* rfee = reg_fee;
* afee = act_fee;
}
Previous
Table of Contents
Next
Copyright © NEWS/400 Books
Wyszukiwarka