C Language Reference for Script Programmers - Polymorphism

Polymorphism

Overview                                                                                                                                                                     

Polymorphism is the ability of one object to take on many different forms . In the case of C programming this has to do with a variable taking on many different types. The way we do this is to create a pointer that doesn’t have any type specified, then when we need to use it we typecast it as a particular type.

This is useful because we can create functions that can take a variety of arguments whose types might not be known until run-time. Of course we do run the risk of accidentally typecasting the block of data as the wrong type, which could have disastrous consequences. For this reason, this implementation of polymorphism is loved by some and despised by others. In fact this behavior really sums up all of C: it’s incredibly powerful, but incredibly dangerous. So you need to stay on your toes when implementing polymorphism to avoid casting to the wrong type.

Syntax                                                                                                                                                                        

To create a pointer that can point to anything you will use the void type. Now you can’t have a variable of type void, but you can have a variable of type void *. Now we already know how to typecast so let’s look at an example:

void *     data = NULL;
int        myInt = 5;
float      myFloat = 3.14;
char       myChar = ‘h’; 

data  = (void *)&myInt;
myInt = *( (int *)data ); 

data = (void *)&myFloat;
myFloat = *( (float *)data ); 

data = (void *)&myChar;
myChar = *( (char *)data ); 

Ok, first line I create a variable called data of type void *, this variable is initialized to NULL. I haven’t mentioned NULL before, this is a macro that is defined in many header files and has a value of 0x0. You always want to initialize a pointer of any kind to NULL, and when you are done with the pointer set it’s value back to NULL. This will help to avoid accidentally trying to access memory that is invalid.

Next point of interest is the 5th line where I assigned the address of myInt to data. Everything is the same as a normal pointer address assignment except that this time we have (void *) in front of &myInt. Since the address of myInt is of type int *, we need to typecast it to void * so that it can be assigned to data. Now in the line that follows we assign the value back to myInt (just to demonstrate how to access the information pointed to by data). First we must typecast data to an int *, then to get the value we need to dereference the pointer. I’ve enclosed the typecast and data in parentheses so that the cast is done first, then that whole thing is dereferenced. The process is then repeated for the other two variables.

Malloc                                                                                                                                                                          

Now I promised myself I wouldn’t get into the Standard Library in this document, but one of the functions in the standard library demonstrates polymorphism quite well; and besides you’ll use it all the time. The function malloc is used to allocate memory at runtime. I’m not going to get too into memory allocation here, see the reference section for books on the subject. All I’m going to do here is show how to use this function and how and why it supports polymorphism.

Malloc is used to allocate memory on the fly. So let’s say your program will need to store a whole bunch of ints but you won’t know how many until the program gets going. You could just define an array that is much bigger than you could ever need, but that’s wasteful. A better way is to allocate the memory for the array after you know how big it should be. The Standard C Library provides a function to do just that, it’s defined in the header file malloc.h and has the following prototype:

void *     malloc     (int size);

Dynamic memory is always referred to with a pointer, in this case a void pointer so that we only need one function to allocate all memory. Otherwise we’d need a function to allocate ints and another one to allocate floats. We’d need a function to allocate memory for any possible data type, which would make it impossible to allocate memory for user defined types. So instead we just have a return type of void *.

The argument is an integer that indicates the size in bytes of the memory to be allocated. Instead of explicitly setting the size in bytes we often use the sizeof  keyword which will give us the size in bytes of a type. So a typical use of malloc might look something like:

int *myIntArray = NULL;

myIntArray = (int *)malloc( 20 * sizeof(int) );
if(!myIntArray)
{
    
// memory allocation error
}

The first line defines an int * and initializes it to NULL. The second line calls malloc and requests 20 * sizeof( int) bytes of memory. That is, it’s requesting enough memory for 20 integers, we can then treat myIntArray as an array of 20 integers. Since the variable we’re assigning this address to is an int * we must typecast the return value of the malloc function from a void * to an int *.

It’s common practice for a function that returns a pointer of any kind to return NULL if it encounters a problem. Malloc is no exception, so immediately after calling malloc we need to test the return value to make sure that it succeeded. We do this in an if statement, testing the value !myIntArray. We remember from our discussion on bit fields that the ! operator is the logical NOT operator. So if our variable had a value of NULL (remember that’s a value of 0x0), then the !  operator would make that value a non-zero value. Likewise if the value was non-zero (as in the case of a valid address) the ! operator would make it 0. Since if treats a value of 0 as false,  and a non-zero value as true; the section of code that follows will only get executed if the malloc function fails. Here we could do any cleanup that is necessary or terminate the program.

Converted from CHM to HTML with chm2web Pro 2.82 (unicode)