Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Advanced C 1992

.pdf
Скачиваний:
92
Добавлен:
17.08.2013
Размер:
4.28 Mб
Скачать

Part II • Managing Data in C

Summary

In this chapter, you learned about structures and unions.

A structure is a group of related data objects that are stored in a contiguous block of memory and can be referred to collectively by a given name.

A union is a group of (related) data objects that share a single block of memory and can be referred to collectively by a given name.

In a union, usually only one member at a time contains valid data.

The typedef keyword enables the programmer to define new data types. These new data types can be simple variables, arrays, structures, or unions.

A bit field is defined as part of a structure. It consists of a named variable whose length is defined as a specific number of bits.

The offsetof() macro returns the offset of a structure’s member, from the beginning of the structure.

226

CDynamic MemoryCAllocation CC C C

C8C C

8 C C C

C C C

C C C

Dynamic Memory

Allocation

Allocating large data objects at compile time is seldom practical—especially if the data objects are used infrequently and for a short time. Instead, you usually allocate these data objects at runtime.

To make more memory available to the programmer, ANSI C offers a number of memory allocation functions, including malloc(), realloc(), calloc(), and free(). Many compiler suppliers complement these functions with similar functions that optimize the allocation of memory based on the specifics of your computer’s architecture. In this chapter, you look at these four functions and Microsoft’s enhanced versions of them.

227

Part II • Managing Data in C

Using the malloc() Function

The memory allocation functions in Table 8.1 include both the ANSI C standard malloc() functions and Microsoft’s extensions.

Table 8.1. Microsoft C malloc() functions.

Function

Description

void * malloc( size_t size );

The ANSI C standard memory allocation function.

void _ _based( void ) *_bmalloc ( _ _segment seg, size_t size );

Does based memory allocation. The memory is allocated from the segment you specify.

void _ _far *_fmalloc( size_tsize );

Allocates a block of memory

 

outside the default data segment,

 

returning a far pointer. This

 

function is called by malloc()

 

when the large or compact

 

memory model is specified.

void _ _near *_nmalloc

Allocates a block of memory inside

( size_t size );

the default data segment, returning

 

a near pointer. This function is

 

called by malloc() when the small

 

or medium memory model is

 

specified.

 

 

The malloc() library function allocates a block of memory up to the size allowed by size_t. To use malloc(), you must follow a few simple rules:

The malloc() function returns a pointer to the allocated memory or NULL if the memory could not be allocated. You should always check the returned pointer for NULL.

The pointer returned by malloc() should be saved in a static variable, unless you are sure that the memory block will be freed before the pointer variable is discarded at the end of the block or the function.

228

Dynamic Memory Allocation

C C C

 

C8C

 

C C C

 

C

You should always free a block of memory that has been allocated by malloc() when you are finished with it. If you rely on the operating system to free the block when your program ends, there may be insufficient memory to satisfy additional requests for memory allocation during the rest of the program’s run.

Avoid allocating small blocks (that is, less than 25 or 50 bytes) of memory. There is always some overhead when malloc() allocates memory—16 or more bytes are allocated in addition to the requested memory.

The malloc() function requires only one parameter: the size of the block of memory to allocate. As mentioned, the length of this parameter is size_t, which on many systems is a short int (16 bits).

You could assume that you cannot allocate a block of memory larger than the ANSI C maximum of 32,767 bytes. Another method is to check the defined identifier (usually in malloc.h) for the maximum for the particular system. With Microsoft C compilers, for example, the maximum is approximately 65,500 bytes. If you assume the worst case (the ANSI C value), however, your program has a better chance of working if the limit changes.

The constraint on the size of a data object may seem unreasonable, but you will rarely reach the 32K limit imposed by ANSI C. If you have large data objects, it is always possible (and desirable) to break them into smaller, more manageable pieces.

If you are determined to define a data object larger than the allowed size (something I do not recommend) and are using a Microsoft C compiler, you can use the halloc() function. This function allocates an array that can be any size (up to the amount of available free memory). You must define the array element size as a power of two, which is not an issue if the array is type char, int, or long. If the array is a structure, type union, or a floating-point long double, this constraint may need to be addressed with padding. If you use the halloc() function, your code will not be portable, but you could probably create a workaround if necessary.

When you use the malloc() function, remember that the block of allocated memory is not initialized. If you want initialized memory, use memset() after the memory is allocated or use calloc() (discussed in the next section). I recommend that you always initialize any memory allocated with the malloc() function.

Listing 8.1, MALLOC2.C, allocates blocks of memory. There is no way to determine the size of the largest available block, so the program begins with the largest size (32,767). If malloc() fails, the program reduces this size by 50 percent; this

229

Part II • Managing Data in C

continues until the requested size is less than 2 bytes. The program stops when there is no more memory, or a total of 2M has been allocated.

Listing 8.1. MALLOC2.C.

/* MALLOC2, written 1992 by Peter D. Hipson * This program allocates memory.

*/

#include

<io.h>

//

I/O functions

#include <stdio.h>

//

Make includes first in program

#include <string.h>

//

For string functions

#include <malloc.h>

// For memory allocation functions

int main(void); // Define main() and the fact that this

 

// program doesn’t use any passed parameters

int main()

 

 

{

 

 

 

int

i = 0;

 

 

int

j = 0;

 

 

int

*nPointer[100] = {NULL};

int

nSize = 32767;

 

long

lTotalBytes

= 0;

while(nSize > 0

&&

// Make nSize valid

 

nSize <= 32767

&&

 

lTotalBytes

< 2000000) // Not more than 2M will be allocated

{

 

 

 

 

nPointer[i]

= (int *)malloc(nSize);

if (nPointer[i] != NULL)

{

++i;

lTotalBytes += nSize;

230

Dynamic Memory Allocation

printf(“Allocated

%5u bytes, total %10ld\n”,

nSize,

 

lTotalBytes);

 

}

else

{

printf(“Couldn’t allocate %5u bytes, total %10ld\n”, nSize,

lTotalBytes);

nSize /= 2;

}

}

for (j = 0; j < i; j++)

{

free(nPointer[j]); nPointer[j] = NULL;

}

return (0);

}

C C C

C8C C

C C C

Listing 8.1 is system dependent. If you are using a PC under DOS in real mode, for example, about 400,000 bytes of memory might be allocated. Under a protectedmode environment such as OS/2 or Windows, you can allocate much more memory. For example, on a system running in protected mode with 10M of free memory, 2M of memory might be allocated, as follows:

Allocated

32767

bytes, total

32767

Allocated

32767

bytes, total

65534

Allocated

32767

bytes, total

98301

and so on...

 

 

 

Allocated

32767

bytes, total

1966020

Allocated

32767

bytes, total

1998787

Allocated

32767

bytes, total

2031554

If you are not sure of the environment in which your application will be running, assume the worst case—less than 32K of free memory.

231

Part II • Managing Data in C

Notice that a loop at the end of the program frees the memory that malloc() has allocated. This loop is performing housekeeping—something that every well-written program should do.

Using the calloc() Function

Because malloc() does not initialize memory and calloc() does, programmers often prefer calloc(). When using Microsoft’s C compilers, the array memory allocation functions in Table 8.2 are used with calloc().

Table 8.2. Microsoft C calloc() Functions.

Function

Description

void *calloc( size_t num, size_t size );

void _ _based( void ) *_bcalloc( _ _segment seg, size_t num, size_t size );

void _ _far *_fcalloc

( size_t num, size_t size );

The ANSI C standard array memory allocation function.

Does based memory allocation. You provide the segment that the data will be allocated from.

Allocates a block of memory outside the default data segment, returning a far pointer. This function is called by calloc() when the large or compact memory model is specified.

void _ _near *_ncalloc ( size_t num, size_t size );

Allocates a block of memory inside the default data segment, returning a near pointer. This function is called by calloc() when the small or medium memory model is specified.

The calloc() library function allocates memory much like the malloc() function, with two main differences. With the calloc() function, you specify two parameters, not one: the number of elements and the size of each element. The product of these parameters determines the size of the memory block to allocate, and must fit in type size_t, which on many systems is a short int (16 bits). If you specify an element

232

Dynamic Memory Allocation

C C C

 

C8C

 

C C C

size of 1, the calloc() num parameter functions similarly to the malloc()

C

size

parameter.

 

The second difference is that the calloc() function initializes the memory it allocates to zero. The value used to initialize the memory is an absolute zero, which usually—but not always—evaluates to a floating-point zero or a NULL pointer value. This is fine if the memory will be used for string storage or integers. If the memory will be used for floating-point values, you should explicitly initialize the block of memory after calloc() returns. I recommend that you always initialize memory allocated with calloc if you do not know the format of the data that you will be storing in it.

To use calloc(), you follow the same rules for using malloc(). These rules are outlined in the first section, “Using the malloc() Function.”

Listing 8.2, CALLOC1.C, allocates blocks of memory. The size of the largest available block cannot be determined, so the program begins with the largest size possible (using the size of int) and tries to allocate an array of 32,767 members. If calloc() fails, the program reduces the size by 50 percent; this continues until the requested size is less than 2 bytes. The program allocates buffers, each containing 32,767 2-byte integers. When an allocation request fails, the program decreases the size of the array until more memory can be allocated. It stops when there is no more memory or 2M have been allocated. The only major difference between MALLOC2.C and CALLOC1.C is the call to the memory allocation function.

Listing 8.2. CALLOC1.C.

/* CALLOC1, written 1992 by Peter D. Hipson * This program allocates arrays of memory.

*/

#include <stdio.h> // Make includes first part of file #include <string.h> // For string functions

#include <malloc.h> // For memory allocation functions

int main(void); // Define main() and establish that this

// program does not use any passed parameters

int main()

continues

233

Part II • Managing Data in C

Listing 8.2. continued

{

 

 

int

i = 0;

 

int

j = 0;

 

int

*nPointer[100] = {NULL};

int

nSize = 32767;

 

long

lTotalBytes = 0;

 

 

while(nSize > 0 &&

// Make nSize valid

 

nSize <= 32767 &&

 

lTotalBytes < 2000000) // No more than 2M will be allocated

{

nPointer[i] = (int *)calloc(nSize, sizeof(int));

if (nPointer[i] != NULL)

{

++i;

lTotalBytes += (nSize * sizeof(int));

printf(“Allocated

%5u short int, total %10ld\n”,

nSize,

 

lTotalBytes);

 

}

else

{

printf(“Couldn’t allocate %5u short int, total %10ld\n”, nSize,

lTotalBytes);

nSize /= 2;

}

}

for (j = 0; j < i; j++)

{

234

Dynamic Memory Allocation

C C C

 

C8C

 

C C C

 

C

free(nPointer[j]); nPointer[j] = NULL;

}

return (0);

}

When CALLOC1 was run, it could not allocate an integer array of 32,767 members, as the following output shows:

Couldn’t Allocate 32767 bytes, total

0

Allocated

16383

bytes, total

32766

Allocated

16383

bytes, total

65532

Allocated

16383

bytes, total

98298

and so on...

 

 

 

Allocated

16383

bytes, total

1965960

Allocated

16383

bytes, total

1998726

Allocated

16383

bytes, total

2031492

The reason for this is not the ANSI C limit of 32,767 bytes in a data object—my C compiler does not enforce this limit. The limit in my compiler is that a data object created by calloc() or malloc() cannot be larger than 65,510 bytes. The array of integers consisted of 32,767 members (each 2 bytes long), for a total of 65,534 bytes, which is too large.

CALLOC1 then attempted to allocate the next size, 16,383, and was successful.

Using the free() Function

The free() functions in Table 8.3 can be used with a Microsoft C compiler.

Table 8.3. Microsoft C free( ) Functions.

Function

Description

void free( void *memblock );

The ANSI C standard array memory deallocation function.

continues

235