042 046


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. C’s 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 code’s 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 can’t catch with pointers (e.g., C lets you change an address in a pointer variable, but you can’t 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. I’ve 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 what’s going on. If you’re 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 isn’t 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 isn’t 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 won’t 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 you’re 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 ); } C’s a Real Nowhere, Man If you’ve ever watched Wile E. Coyote spinning his legs in thin air above some canyon floor, you’ve seen what happens when you try to use a C pointer that doesn’t point to anything. C’s macro name for this ticket to nowhere is NULL. It isn’t 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, it’s value is initially NULL (or some undefined value). Thus, the assignment statement’s target doesn’t 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 you’re 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 you’re 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 Can’t Get There from Here In C, you can also create “dangling” pointers — ones that point somewhere, but not where you’d expect. Figure 6.5 shows a function intended to return a month’s name, given the month’s 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 function’s 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 Pascal’s built-in new pointer function offer some help. To conveniently and safely allocate a new string and assign it a value, first use C’s typedef feature to make a meaningful name for C’s “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 046
v 03 046
all27 12 042
v 02 046
The Modern Dispatch 046 Pulp Villains
0423?j mi dzień
046 047
Lux Lesebogen 042 Von Tretrad zur Turbine
046 Ustawa o zawodach lekarza i lekarza dentysty

więcej podobnych podstron