Previous | Table of Contents | Next |
Figure 6.6 uses string_t to declare parameters for the function new_string. (A similar function, strdup, is available in Microsoft C but not in ANSI C.) With this function, its easy to write a correct version of month_name, as shown in Figure 6.7. Like Figure 6.5s incorrect version, the corrected version returns a character pointer. However, the pointer returned by the corrected version points to memory that remains allocated and that contains the desired month name.
Figure 6.6 new_string Function
string_t function new_string( const string_t val ) { /* | Allocate and load storage for string val | | Return pointer or NULL, if error */ string_t p; if ( val == NULL ) { printf( "Invalid NULL value pointer\n" ); return NULL; } p = (string_t) malloc( strlen( val ) + 1 ); if ( p == NULL ) { printf( "No memory for %s\n", val ); return NULL; } else { strcpy( p, val ); return p; } }
Figure 6.7 Corrected month_name Function
char * month_name( const int month ) { /* | Return month name */ char names[10][12] = { "January", ... "December" }; return new_string( names[ month - 1 ] ); }
In large or long-running programs that explicitly allocate memory by calls to Cs malloc memory allocation function, you may try to allocate more memory than is available. If malloc cant allocate the amount of memory you request, it returns NULL. Always check for a NULL return value after calling malloc. By doing this, you can avoid the problems that occur when you use a NULL pointer. Most C programmers follow this rule most of the time. But thats not good enough. Even if you allocate only one byte as the very first statement in a trivial main block (an operation you think can never fail), check what malloc returns. The test takes 30 seconds to code and practically no time to be executed, and youll never be unpleasantly surprised when the it couldnt happen does.
If a program does much explicit memory allocation, you also need to guard against Cs form of amnesia unexpected memory loss or memory leakage. This quaint term refers to the situation where memory youve allocated isnt available for reuse when youre done with it.
Figure 6.8 shows how leakage can occur. The first malloc operation allocates a memory block and stores a pointer to it in ptr. After using this memory to hold a character string, the code reuses ptr to point to memory containing a different string. This code will be executed fine, but the memory originally allocated to hold the first string will remain marked in use, even though it cant be referenced or deallocated after the second malloc operation (assuming the pointer value isnt copied to another pointer variable).
Figure 6.8 How Memory Leakage Occurs
string_t val1 = "abc"; string_t val2 = "xyz"; string_t ptr; ptr = (string_t) malloc( strlen( val1 ) + 1 ); strcpy( ptr, val1 ); . . . ptr = (string_t) malloc( strlen( val2 ) + 1 ); strcpy( ptr, val2 ); . . .
A simple solution is the allocate macro in Figure 6.9, which uses malloc when the pointer is NULL and realloc when its not. (In a subsequent chapter, Ill explain why allocate uses so many parentheses. Simply put, the parentheses prevent unintended changes in the generated codes evaluation order.) Figure 6.10 shows how to reuse allocated memory with the allocate macro. Note that allocate requires the pointer to be NULL or a value returned by one of the memory allocation functions. Another rule, always initialize pointers in their definitions, partially satisfies this requirement. Although C initializes static pointer variables to NULL, it doesnt initialize automatic pointer variables. Coding an explicit NULL initializer covers all cases and emphasizes pointer declarations.
Figure 6.9 allocate Macro
/* | ptr MUST be NULL or address of block allocated | by calloc, malloc, or realloc functions | | The value of allocate is either a valid pointer | of the specified ptr_type, or NULL if no memory | can be allocated. */ #define allocate( ptr, ptr_type, alloc_size ) \ \ ( ( ptr ) = ( ptr_type ) ( ( ( ptr ) == NULL ) ? \ malloc( ( alloc_size ) ) : \ realloc( ( ptr ), ( alloc_size ) ) ) )
Figure 6.10 Avoiding Memory Leakage
string_t val1 = "abc"; string_t val2 = "xyz"; string_t ptr = NULL; allocate( ptr, string_t, ( strlen( val1 ) + 1 ) ); strcpy( ptr, val1 ); . . . allocate( ptr, string_t, ( strlen( val2 ) + 1 ) ); strcpy( ptr, val2 ); . . .
Previous | Table of Contents | Next |