Previous Table of Contents Next


Pointers are convenient for writing string-handling functions. For example, you could write your own version of the strcpy function. In practice, this is not necessary, because the standard library provides this function. However, it is useful to see how easy such a function is to write in C++.

char *strcpy (char *dest, char *src) {
   while (*src != ‘\0’) {
      *dest = *src;
      dest++;
      src++;
}
*dest = ’\0’; // Add terminating null
return dest;
}

There are at least a couple of ways to make this code more compact, although this version is probably the easiest to understand. (However, as an exercise, you can apply some of the techniques in the previous section to reduce the size by at least a couple of lines.) In any case, Figure 3.9 illustrates how the function works.


Figure 3.9  How the strcpy function works.

Pointers and Dynamic Memory

Dynamic is one of those terms that get tossed around (even abused) a lot in the programming world. In general, dynamic means “in motion” or “changing.”

Dynamic memory is used when your memory requirements may change during running of the program. If you’re reading the contents of a file into memory, for example, the amount of memory you need depends on the size of the file. If you’re building a linked list of structures, you need to allocate new memory every time you add a new structure.

C++ provides two approaches to dynamic memory. You can use the malloc family of functions inherited from the C standard library, or you can use the new and delete operators, which are supported in C++ only.

In both cases, you have to understand and use pointers. With ordinary data structures, the compiler assigns an address to the variable or object. You never need to know this address; instead, you just use the variable’s name.

Using malloc and free (C and C++)

Calling the malloc function is a way of asking the operating system to give you a chunk of memory of a specific size. The operating system will oblige you if it possibly can (but note that in serious programs, you should test for error conditions). The result is that you get a pointer to this chunk, which consists of consecutive bytes in memory.

Using the malloc and free library functions involves the following steps:

1.  Include the header file, MALLOC.H:
#include <malloc.h>
2.  After declaring a pointer of the appropriate type, call the malloc function, specifying as the argument the number of bytes needed. Apply a pointer cast, which changes the type to the appropriate kind of pointer, and then assign the result to the pointer:
pointer = (type *) malloc (elements * sizeof (type));
3.  When you are finished with the memory, call the free function and give the pointer as the argument:
free (pointer);

In terms of programming techniques, the use of dynamic memory differs from using ordinary variables in one important way: when using dynamic memory, you have no direct access to the data. You have access only through a pointer. Fortunately, it is easy to refer to any part of a memory block through a pointer. In C++ (and in C), pointers and array names are almost interchangeable. Once the operating system allocates the chunk of memory, the operating system returns a pointer giving the starting address of this chunk.

Don’t lose this pointer (or change its value without first saving it), because otherwise all ties to the memory are lost.

For example, the following code allocates a memory block of 500 integers (int). After malloc returns successfully, you can refer to any of the integers as members of an array. Use array indexing on the pointer itself.

#include <malloc.h>
#include <stdio.h>
...
int *p;

p = (int *) malloc (500 * sizeof (int));

if (p) {
   p[0] 5;           // Assign 5 to first element
   p[50] = -33;      // Assign -33 to 51st element
   printf (“The 51st element is %d.\n”, p[50]);
   ...
   free (p);
}

The malloc statement takes one argument: the number of bytes requested. Because room for 500 integers is wanted, the appropriate size is 500 times the size of each integer. The sizeof operator (which admittedly looks like a function) is a special built-in operator in C and C++ that returns the size of a type.

p = (int *) malloc (500 * sizeof (int));

This statement also uses a data cast, (int *), to change the type of the value returned. The malloc function returns a pointer of type void*, which is a generic pointer type. In C++, you must change this data type before assigning it. The data cast changes the type from void* to int* so that it matches the type of the pointer p.

(int *) expression

As another example, the following code allocates a block of 100 double-precision floating-point numbers (double):

double *p;

p = (double *) malloc (100 * sizeof (double));
Requiring the data cast is one area where C++ adds restrictions to C. In C, you can be sloppy and freely assign between pointers of different types. In C++, you must explicitly change the data type of a void* pointer before assigning it to another type of pointer. The C++ approach is safer, because it prevents you from accidentally assigning between different kinds of pointers. Using an int* pointer to access an array of double, for example, is guaranteed to cause errors unless you are doing it deliberately and for good reason.

After the malloc function returns, it is a good idea to test the result. If the function was successful, the pointer contains a valid address. Otherwise, the pointer is assigned the null value, which is numerically equal to zero. An if statement interprets a null value as false. The pointer value is zero (false) if the malloc function failed because of insufficient memory. A “true” value therefore indicates success.

if (p) {
    p[0] = 5;         // Assign 5 to first element
    p[50] = -33;     // Assign -33 to 51st element
    printf (“The 51st element is %d.\n”, p[50]);
    ...
    free (p) ;
}

The final thing the code does is to explicitly free the memory block by calling the free function. This is the other key difference between dynamic memory and ordinary variables. If you don’t get into the habit of freeing dynamic memory blocks, then your code is likely to eat up memory without returning it, resulting in memory leaks to the system. You don’t want to do that!

Using now and delete (C++ Only)

The new and delete operators can be effectively used wherever you use the malloc and free functions. If you understand how to call malloc, using new is easy. The use of new and delete also offers other advantages:

  You don’t have to include a header file just to declare new and delete.
  You don’t have to use a type cast before assigning to a pointer. The new operator automatically returns the right kind of pointer.
  Most important, new and delete do more than simply allocate chunks of memory. When you allocate an object using new, it automatically calls the object’s constructor, if any. Similarly, freeing memory using delete automatically calls a destructor if appropriate. In Chapter 5, you will work with objects that have constructors and destructors.

In summary, then, you need to use new and delete if you are going to work with classes and object orientation in C++; otherwise, you can use malloc.

To allocate memory using new, place the type of the object after new. This type can be a primitive type, a class, or an array.

pointer = new type;

So, for example, you could allocate a single integer or an array of 500 integers:

int *p1, *p2;

p1 = new int;          // p1 points to a single integer
p2 = new int[500];     // p2 points to first of 500
                      // integer

In C++, you can combine initialization with operators and function calls so that you can use new in the same statement that declares the pointer:

int *p2 = new int[500];

To free memory allocated with new, use the delete operator followed by a pointer name. If the memory involved the creation of an array (as in the immediately preceding example), then place empty brackets ([]) before the pointer name.

Therefore, use one of the following two statements, as appropriate.

delete pointer; (if one object was created with new)
delete [] pointer; (if array was created with new)

The example in the previous section could have been written this way using new and delete:

#include <stdio.h>
...
int *p new int[500];

if (p) {

p[0] = 5;         // Assign 5 to first element
p[50] = -33;      // Assign -33 to 51st element
printf (“The 51st element is %d.\n”, p[50]);
...
delete p;
}

Note that the new operator returns a null value if there is insufficient memory, just as malloc does. For best results, you should test this value before using or deleting the new memory.


Previous Table of Contents Next