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
A companion to the previous rule is use array notation instead of pointers and dereferencing when working with arrays. Cs array notation is really just shorthand for pointer operations, and C lets you use either in most contexts. For example, if a is declared as an array, *(a+i), a[i], and i[a] mean exactly the same thing.
But when using an array variable, you should stick with array notation such as a[i] to keep your codes meaning obvious. An added benefit in using such notation is that, in some contexts, the C compiler can catch mistakes in expressions using array names that it cant catch with pointers (e.g., C lets you change an address in a pointer variable, but you cant change the address referred to by an array name). And before you let some old hand at C convince you that direct manipulation of pointers is so much faster than subscripting arrays, read Pulling a `Fast' One, page XX. In business applications and most utility software, you can freely use array subscripts without performance concerns.
Ive read the viewpoint that since C array notation is really just shorthand for pointer operations, you should use pointer notation because it more honestly shows whats going on. If youre trying to dissuade someone from using C, this argument has merit. C pointer and dereferencing notation certainly looks stranger than array notation to most programmers and warns newcomers that C isnt your ordinary HLL. But in the long run, array notation expresses high-level data constructs much better than pointer notation.
Finger Pointing
You can fall into another C pothole by forgetting that a pointer isnt the same as the thing it points to. The following code appears to save a copy of the current string in a previous string variable and then assign the current string a new value.
char * curstr;
char * prvstr;
curstr = (char *) malloc( 10 );
prvstr = (char *) malloc( 10 );
strcpy( curstr, "abc" );
prvstr = curstr;
strcpy( curstr, "xyz" );
But after these statements are executed, both curstr and prvstr point to xyz. The assignment prvstr = curstr copies the address stored in curstr to prvstr, not the contents of the memory location curstr points to.
Using *prvstr = *curstr wont accomplish what we want either. It just copies the single byte that curstr points to into the single byte that prvstr points to. To do a simple save a copy of this string operation, you require code like that in Figure 6.4. As is often the case in C, high-level operations that should be simple and safe are neither. I can offer only this caution: When working with pointers in assignment statements, double-check that youre using the right level of indirection. Most C compilers warn you about incompatible types or different levels of indirection in assignments, but they give no warning when both sides of an assignment are compatible types and levels but at the wrong level (as in the previous example).
Figure 6.4 Saving a Copy of a String
prvstr = (char *) malloc( strlen( curstr ) + 1 );
if ( prvstr == NULL ) {
printf( "No memory available\n" );
}
else {
strcpy( prvstr, curstr );
}
Cs a Real Nowhere, Man
If youve ever watched Wile E. Coyote spinning his legs in thin air above some canyon floor, youve seen what happens when you try to use a C pointer that doesnt point to anything. Cs macro name for this ticket to nowhere is NULL. It isnt hard to accidentally create null pointers; in fact, you get one every time you define a static pointer variable. Look at the following code:
int val = 25;
int *ptr;
*ptr = val;
This code will be compiled but will either blow up or corrupt memory at runtime. Although ptr is defined as a pointer, its value is initially NULL (or some undefined value). Thus, the assignment statements target doesnt have a valid address. The two correct alternatives are:
ptr = &val;
which assigns the address of val to ptr, or
ptr =
(int *) malloc( sizeof( val ) );
*ptr = val;
which (usually, as I explain in the next section) allocates memory to store the value 25. If youre counting on one of my magic macros to avoid this pitfall, you may be disappointed that I can offer only the shopworn C programming dictum: Be careful! Unless youre positive a pointer has been initialized, check it for NULL, as shown below, before using it:
if ( ptr NE NULL ) {
*ptr = val;
}
else {
printf( "ptr is NULL\n");
}
Some compilers let you generate checks for referencing uninitialized variables or NULL pointers, and you may want to use this defense both during development and for production applications.
You Cant Get There from Here
In C, you can also create dangling pointers ones that point somewhere, but not where youd expect. Figure 6.5 shows a function intended to return a months name, given the months number. This function will be compiled, will run, and will return a non-null character pointer. But the returned pointer will point to memory allocated only temporarily to the names array. When the month_name function returns, the names array will be deallocated, and another functions local (automatic) variables may reuse its memory. Using the pointer value returned by month_name may result in a month Pope Gregory never contemplated or worse, another frozen machine. Functions patterned after Pascals built-in new pointer function offer some help. To conveniently and safely allocate a new string and assign it a value, first use Cs typedef feature to make a meaningful name for Cs string type:
typedef char * string_t;
Here, I follow the common convention of ending typedef names with _t. C typedef names are synonyms for explicit C type specifications and help make your C programs more readable. (In Chapter 4, I describe a more comprehensive way to deal with strings in C. In this chapter, I cover only conventional C string-handling. You can combine suggestions from both chapters to build your own complete string facility.)
Figure 6.5 Creating a Dangling Pointer
char* month_name( const int month ) {
/*
| Return month name
*/
char names[10][12] = {
"January", ... "December" };
return names[ month - 1 ];
}
Previous
Table of Contents
Next
Copyright © NEWS/400 Books
Wyszukiwarka
Podobne podstrony:
tekst 046v 03 046all27 12 042v 02 046The Modern Dispatch 046 Pulp Villains0423?j mi dzień046 047Lux Lesebogen 042 Von Tretrad zur Turbine046 Ustawa o zawodach lekarza i lekarza dentystywięcej podobnych podstron