Search This Blog

Tuesday, April 28, 2020

Throwback - Functions

Migrating Content from even an older blog.

The following content is from my even older blog which had to be removed due to discontinuation of the platform. I am posting it here as a new content for this blog.

NOTE

This posts discusses enhancements in the capabilities of functions that have been made to C++.  These changes make programming more convenient and permit the compiler to do further checking for errors.  A fair amount of time is also spent in this chapter teaching the modern form of function definition and prototyping.

Prototyping allows the compiler to do additional type checking for your function calls which can detect some programming errors.  Prototyping is a relatively new addition to C, so even some experienced C programmers are not familiar with it. 

PROTOTYPES

Examine the file named PROTYPE1.CPP for our first look at a prototype and an illustration of how it is used. 
// PROTYPE1.CPP
#include <iostream.h>

4 void do_stuff(int wings, float feet, char eyes);

void main()
{
8              int arm = 2;
                float foot = 1000.0;
                char lookers = 2;

12           do_stuff(3, 12.0, 4);
13           do_stuff(arm, foot, lookers);
}

void do_stuff(int wings, float feet, char eyes)
{
18           cout << "There are " << wings << " wings." << "\n";
                cout << "There are " << feet << " feet." << "\n";
                cout << "There are " << (int)eyes << " eyes." << "\n\n";
}




// Result of execution
//
// There are 3 wings.
// There are 12 feet.
// There are 4 eyes.
//
// There are 2 wings.
// There are 1000 feet.
// There are 2 eyes.

The prototyping used in C++ is no different than that used in ANSI-C.  Actually, many C programmers take a rather dim view of prototyping and seem reluctant to use it, but with C++ it is considerably more important and is in much heavier use.  In fact, prototyping is required to be used in some situations in C++.

A prototype is a limited model of a more complete entity to come later.  In this case, the full function is the complete entity to come later and the prototype is illustrated in line 4.  The prototype gives a model of the interface to the function that can be used to check the calls to the function for the proper number of parameters and the correct types of parameters.  Each call to the function named do_stuff() must have exactly three parameters or the compiler will give an error message.  In addition to the correct number of parameters, the types must be compatible or the compiler will issue an error message.  Notice that when the compiler is working on lines 12 and 13, the type checking can be done based on the prototype in line 4 even though the function itself is not yet defined.  If the prototype is not given, the number of parameters will not be checked, nor will the types of the parameters be checked.  Without a prototype, if you have the wrong number of parameters, you will get an apparently good compile and link, but the program may do some very strange things when it is executed.

To write the prototype, simply copy the header from the function to the beginning of the program and append a semicolon to the end as a signal to the compiler that this is not a function but a prototype.  The variable names given in the prototype are optional and act merely as comments to the program reader since they are completely ignored by the compiler.  You could replace the variable name wings in line 4 with your first name and there would be no difference in compilation.  Of course, the next person that had to read your program would be somewhat baffled with your choice of variable names.

In this case, the two function calls to this function, given in lines 12 and 13, are correct so no error will be listed during compilation.

Even though we wish to use the char type for eyes in the function, we wish to use it as a number rather than as a character.  The cast to int in line 20 is required to force the printout of the numerical value rather than an ASCII character.  The next example program is similar but without the cast to int.


COMPATIBLE TYPES

We mentioned compatible types earlier so we should review them just a bit in order to make our discussion of prototyping complete.  Compatible types are any simple types that can be converted from one to another in a meaningful way.  For example, if you used an integer as the actual parameter and the function was expecting a float type as the formal parameter, the system would do the conversion automatically, without mentioning it to you.  This is also true of a float changing to a char, or a char changing to an int.  There are definite conversion rules which would be followed.  These rules are given in great detail in section 3.2 of the draft of the ANSI-C standard and are also given on page 198 of the second edition of the K&R reference.

If we supplied a pointer to an integer as the actual parameter and expected an integer as the formal parameter in the function, the conversion would not be made because they are two entirely different kinds of values.  Likewise, a structure would not be converted automatically to a long float, an array, or even to a different kind of structure, because they are all incompatible and cannot be converted in any meaningful manner.  Likewise, the type specified as the return type, in this case void, must be compatible with the expected return type in the calling statement, or the compiler will issue a warning.

HOW DOES PROTOTYPING WORK?

This is your chance to try prototyping for yourself and see how well it works and what kinds of error messages you get when you do certain wrong things.  Change the actual parameters in line 12  to read (12.2, 13, 12345) and see what the compiler says about that change.  It will probably say nothing because they are all type compatible.  If you change it to read (12.0, 13), it will issue a warning or error because there are not enough arguments given.  Likewise you should receive an error message if you change one of the parameters in line 13 to an address by putting an ampersand in front of one of the variable names.  Finally, change the first word in line 4 from void to int and see what kind of error message is given.  You will first be required to make the function header in line 16 agree with the prototype, then you will find that there is not a variable returned from the function.  You should have a good feeling that prototyping is doing something worthwhile for you after making these changes.

Be sure to compile and execute this program then make the changes recommended above, attempting to compile it after each change.


A LITTLE MORE PROTOTYPING

Examine the next example program named PROTYPE2.CPP for a little more information on     prototyping.  This program is identical to the last one except for a few small changes. 
// PROTYPE2.CPP
#include <iostream.h>

4 void do_stuff(int, float, char);

void main()
{
8              int arm = 2;
                float foot = 1000.0;
                char lookers = 65;

12           do_stuff(3, 12.0, 67);
13           do_stuff(arm, foot, lookers);
}

16 void do_stuff(int wings,    // Number of wings
              float feet,   // Number of feet
              char eyes)    // Number of eyes
{
                cout << "There are " << wings << " wings." << "\n";
                cout << "There are " << feet << " feet." << "\n";
                cout << "There are " << eyes << " eyes." << "\n\n";
}




// Result of execution
//
// There are 3 wings.
// There are 12 feet.
// There are C eyes.
//
// There are 2 wings.
// There are 1000 feet.
// There are A eyes.

The variable names have been omitted from the prototype in line 4 merely as an illustration that they are interpreted as comments by the C++ compiler.  The function header is formatted differently to allow for a comment alongside each of the actual parameters.  This should make the function header a little more self explanatory.  However, you should remember that comments should not be used to replace careful selection of variable names.  In this particular case, the comments add essentially nothing to the clarity of the program.


WHAT DOES PROTOTYPING COST?

Prototyping is essentially free because it costs absolutely nothing concerning the run time size or speed of execution.  Prototyping is a compile time check only, and slows down the compile time a negligible amount because of the extra checking that the compiler must do.  If prototyping finds one error for you that you would have had to find with a debugger, it has more than paid for itself for use in an entire project. 

The only price you pay to use prototyping is the extra size of the source files because of the prototypes, and the extra time for the compiler to read the prototypes during the compilation process, but both costs are negligible.

Be sure to compile and execute this example program.  You will find that it is identical to the last example program, except for the changes in the prototype and the removal of the cast from the last line of the function.


PASS BY REFERENCE

Examine the program named PASSREF.CPP for an example of a pass by reference, a construct which is not available in ANSI-C.  This example program illustrates a situation where it can be used to your advantage.  The pass by reference allows the passing of a variable to a function and returning the changes made in the function to the main program.  In ANSI-C the same effect can be seen when a pointer to a variable is passed to a function, but use of a reference variable is a little cleaner.
// PASSREF.CPP
#include <iostream.h>
#include <stdio.h>
4 Void fiddle(int in1, int &in2);

void main()
{
9              int count = 7, index = 12;

                cout << "The values are ";
                printf("%3d %3d\n", count, index);

14           fiddle(count, index);

                cout << "The values are ";
                printf("%3d %3d\n", count, index);
}

void fiddle(int in1, int &in2)
{
22           in1 = in1 + 100;
                in2 = in2 + 100;
                cout << "The values are ";
                printf("%3d %3d\n", in1, in2);
}



// Result of execution
//
// The values are    7  12
// The values are  107 112
// The values are    7 112



Observe the prototype in line 4 where the second variable has an ampersand in front of the variable name.  The ampersand instructs the compiler to treat this variable just like it were passed a pointer to the actual variable passed from the calling function.  It acts like the actual variable from the main program is used in the function.  In the function itself, in lines 22 through 25, the variable in2 is used just like any other variable but it acts like we are using the variable passed to this function from the main program not a copy of it.  The other variable named in1 is treated just like any other normal variable in ANSI-C.  In effect, the name in2 is a synonym for the variable named index in the main program, but the name in1 refers to a copy of the variable count from the main program.  In actual practice, a pointer is passed to the function and it is automatically dereferenced when used in the function.  This is transparent to you, the programmer.

If you prefer to omit the variable names in the prototypes, you would write the prototype as follows;

   void fiddle(int, int&);

If you are a Pascal programmer, you will recognize that the variable named in1 is treated just like a normal parameter in a Pascal call, a call by value.  The variable named in2 however, is treated like a variable with the reserved word VAR used in front of it, usually referred to as a call by reference.  As mentioned earlier, the reference variable used in C++ is actually a self dereferencing pointer which refers to, or points to, the original value.

When you compile and execute this program, you will find that the first variable got changed in the function but was returned to its original value when we returned to the main program.  The second variable however, was changed in the function and the new value was reflected back into the variable in the main program which we can see when the values are listed on the monitor.


DEFAULT PARAMETERS

Examine the file named DEFAULT.CPP for an example of the use of default parameters in C++.  This program really looks strange since it contains default values for some of the parameters in the prototype, but these default values are very useful as we will see shortly.
// DEFAULT.CPP
#include <iostream.h>
#include <stdio.h>

5 int get_volume(int length, int width = 2, int height = 3);

void main()
{
9              int x = 10, y = 12, z = 15;

                cout << "Some box data is " << get_volume(x, y, z) << "\n";
                cout << "Some box data is " << get_volume(x, y) << "\n";
                cout << "Some box data is " << get_volume(x) << "\n";

                cout << "Some box data is ";
                cout << get_volume(x, 7) << "\n";
                cout << "Some box data is ";
                cout << get_volume(5, 5, 5) << "\n";
}

int get_volume(int length, int width, int height)
{
22           printf("%4d %4d %4d   ", length, width, height);
                return length * width * height;
}


// Result of execution
//
//   10   12   15Some box data is   1800
//   10   12    3Some box data is    360
//   10    2    3Some box data is     60
// Some box data is   10    7    3   210
// Some box data is    5    5    5   125


This prototype says that the first parameter named length must be given for each call of this function because a default value is not supplied.  The second parameter named width, however, is not required to be specified for each call, and if it is not specified, the value 2 will be used for the variable width within the function.  Likewise, the third parameter is optional, and if it is not specified, the value of 3 will be used for height within the function.

In line 11 of this program, all three parameters are specified so there is nothing unusual about this call from any other function call we have made.  Only two values are specified in line 12 however, so we will use the default value for the third parameter and the system acts as if we called it with get_value(x, y, 3) since the default value for the third value is 3.  In line 13, we only specified one parameter which will be used for the first formal parameter, and the other two will be defaulted.  The system will act as if we had called the function with get_volume(x, 2, 3).  Note that the output from these three lines is reversed.  This will be explained shortly.

There are a few rules which should be obvious but will be stated anyway.  Once a parameter is given a default value in the list of formal parameters, all of the remaining must have default values also.  It is not possible to leave a hole in the middle of the list, only the trailing values can be defaulted.  Of course, the defaulted values must be of the correct types or a compiler error will be issued.  The default values can be given in either the prototype or the function header, but not in both.  If they are given in both places, the compiler must not only use the default value, but it must carefully check to see that both values are identical.  This could further complicate an already very complicated problem, that of writing a C++ compiler.
As a matter of style, it is highly recommended that the default values be given in the prototype rather than in the function. 

WHY IS THE OUTPUT SCRAMBLED?

When the compiler finds a cout statement, the complete line of code is initially scanned from right to left to evaluate any functions, then the data is output field by field from left to right.  Therefore in line 11, get_value() is evaluated with its internal output displayed first.  Then the fields of the cout are displayed from left to right with "Some box data is" displayed next.  Finally, the result of the return from get_value() is output in int format, the type of the returned value.  The end result is that the output is not in the expected order when lines 11 through 13 are executed.  (The output is not what you would intuitively expect to happen so appears to be a deficiency in the language.  A call to Borland International, the writers of Turbo C++ and Borland C++, verified that this is operating correctly.)

Lines 15 through 18 are similar to any two of the lines of code in lines 11 through 13, but are each separated into two lines so the output is in the expected order.

Be sure to compile and execute DEFAULT.CPP after you understand it.

VARIABLE NUMBER OF ARGUMENTS

Examine the program named VARARGS.CPP for an illustration of the use of a variable number of arguments in a function call.
// VARARGS.CPP
#include <iostream.h>
#include <stdarg.h>

            // Declare a function with one required parameter
6 void display_var(int number, ...);

void main()
{
10           int index = 5;
                int one = 1, two = 2;

13           display_var(one, index);
14           display_var(3, index, index + two, index + one);
15           display_var(two, 7, 3);
}

void display_var(int number, ...)
{
20           va_list param_pt;

22           va_start(param_pt, number);              // Call the setup macro

                cout << "The parameters are ";
                for (int index = 0 ; index < number ; index++)
                                cout << va_arg(param_pt, int) << " "; // Extract a parameter
                cout << "\n";
27           va_end(param_pt);                        // Closing macro
}




// Result of Execution
//
// The parameters are 5
// The parameters are 5 7 6
// The parameters are 7 3

We have gone to a lot of trouble to get the compiler to help us by carefully checking how many parameters we use in the function calls and checking the types of the parameters.  On rare occasion, we may wish to write a function that uses a variable number of parameters.  The printf() function is a good example of this.  ANSI-C has a series of three macros available in the "stdarg.h" header file to allow the use of a variable number of arguments.  These are available for use with C++ also, but we need a way to eliminate the stronger type checking that is done with all C++ functions.  The three dots illustrated in line 6 will do this for us.  This prototype says that a single argument of type int is required as the first parameter, then no further type checking will be done by the compiler.

You will note that the main program consists of three calls to the function, each with a different number of parameters, and the system does not balk at the differences in the function calls. 
In fact, you could put as many different types as you desire in the calls.  As long as the first one is an int type variable, the system will do its best to compile and run it for you.  Of course the compiler is ignoring all type checking beyond the first parameter so it is up to you to make sure you use the correct parameter types in this call.

In this case, the first parameter gives the system the number of additional parameters to look for and handle.  In this simple program, we simply display the numbers on the monitor to illustrate that they really did get handled properly.

Of course, you realize that using a variable number of arguments in a function call can lead to very obscure code and should be used very little in a production program, but the capability exists if you need it.  Be sure to compile and execute this program.


FUNCTION NAME OVERLOADING

Examine the file named OVERLOAD.CPP for an example of a program with the function names      overloaded.  This is not possible in ANSI-C, but is perfectly legal and in fact used quite regularly in C++.  At first this will seem a bit strange, but it is one of the keystones of object oriented programming. 
// OVERLOAD.CPP
#include <iostream.h>

// overload do_stuff;          // This may be optional

6 int do_stuff(const int);       // This squares an integer
7 int do_stuff(float);           // This triples a float & returns int
8 float do_stuff(const float, float); // This averages two floats

void main()
{
12           int index = 12;
                float length = 14.33;
                float height = 34.33;

                cout << "12 squared is "    << do_stuff(index)          << "\n";
                cout << "24 squared is "    << do_stuff(2 * index)      << "\n";
                cout << "Three lengths is " << do_stuff(length)         << "\n";
                cout << "Three heights is " << do_stuff(height)         << "\n";
                cout << "The average is "   << do_stuff(length, height) << "\n";
}

int do_stuff(const int in_value)      // This squares an integer
{
25           return in_value * in_value;
}

int do_stuff(float in_value)    // Triples a float & return int
{
30           return (int)(3.0 * in_value);
}

                                      // This averages two floats
float do_stuff(const float in1, float in2)
{
36           return (in1 + in2)/2.0;
}




// Result of execution
//
// 12 squared is 144
// 24 squared is 576
// Three lengths is 42
// Three heights is 102
// The average is 24.330002


You will notice in this example program that there are three functions, in addition to the main function, and all three have the same name.  Your first question is likely to be, "Which function do you call when you call do_stuff()?"  That is a valid question and the answer is, the function that has the correct number of formal parameters of the correct types.  If do_stuff() is called with an integer value or variable as its actual parameter, the function beginning in line 23 will be called and executed.  If the single actual parameter is of type float, the function beginning in line 28 will be called, and if two floats are specified, the function beginning in line 33 will be called.

It should be noted that the return type is not used to determine which function will be called.  Only the types of the formal parameters are used to determine which overloaded function will be called.

The keyword overload used in line 4 tells the system that you really do intend to overload the name do_stuff, and the overloading is not merely an oversight.  This is only required in C++ version 1.2.  C++ version 2.0 and greater do not require the keyword overload but allows it to be used optionally in order to allow the existing body of C++ code to be compatible with newer compilers.  It is not necessary to use this keyword because, when overloading is used in C++, it is generally used in a context in which it is obvious that the function name is overloaded.

When the final C++ standard is completed, the use of this word may not be permitted.  As was mentioned earlier, the C++ language is changing and we must be willing to change with it.

The actual selection of which function to call is done at compile time, not at execution time, so the program is not slowed down.  If each of the overloaded function names were changed to different names, each being unique, there would be no difference in execution size or speed of the resulting program.

Overloading of function names may seem very strange to you, and it is strange if you are used to the rules of K&R or ANSI-C programming.  As you gain experience with C++, you will feel very comfortable with this, and you will use it a lot in your C++ programming.

Note the use of the keyword const used in some of the function prototypes and headers.  Once again, this prevents the programmer from accidentally changing the formal parameter within the function.  In a function as short as these, there is no real problem with an accidental assignment.  In a real function that you occasionally modify, you could easily forget the original intention of the use of a variable and attempt to change it during an extended debugging session.

Monday, April 27, 2020

Throwback - Pointers

Migrating Content from even an older blog.

The following content is from my even older blog which had to be removed due to discontinuation of the platform. I am posting it here as a new content for this blog.

POINTER REVIEW

Examine the program named POINTERS.CPP for a simple example of the use of pointers.
// POINTERS.CPP
#include <iostream.h>

void main()
{
                int   *pt_int;
                float *pt_float;
                int   pig = 7, dog = 27;
                float x = 1.2345, y = 32.14;
                void *general;

                pt_int = &pig;
                *pt_int += dog;
                cout << "Pig now has the value of " << *pt_int << "\n";
                general = pt_int;

                pt_float = &x;
                y += 5 * (*pt_float);
                cout << "y now has the value of " << y << "\n";
                general = pt_float;

                const char *name1 = "John";    // Value cannot be changed
                char *const name2 = "John";    // Pointer cannot be changed
}




// Result of execution
//
// Pig now has the value of 34
// y now has the value of 38.3125

A pointer in either ANSI-C or C++ is declared with an asterisk preceding the variable name.  The pointer is then a pointer to a variable of that one specific type and should not be used with variables of other types.  Thus pt_int is a pointer to an integer type variable and should not be used with any other type.  Of course, an experienced C programmer knows that it is simple to coerce the pointer to be used with some other type by using a cast, but he must then assume the responsibility for its correct usage.

In line 12 the pointer named pt_int is assigned the address of the variable named pig and line 13 uses the pointer named pt_int to add the value of dog to the value of pig because the asterisk dereferences the pointer in exactly the same manner as standard C.  The address is used to print out the value of the variable pig in line 14 illustrating the use of a pointer with the stream output operator cout.  Likewise, the pointer to float named pt_float is assigned the address of x, then used in a trivial calculation in line 18. 

If you are not completely comfortable with this trivial program using pointers, you should review the use of pointers in any good C programming book or Coronado Enterprises C tutorial before proceeding on because we will assume that you have a thorough knowledge of pointers throughout the remainder of this tutorial.  It is not possible to write a C program of any significant size or complexity without the use of pointers.


CONSTANT POINTERS AND POINTERS TO CONSTANTS

The definition of C++ allows a pointer to a constant to be defined such that the value to which the pointer points cannot be changed but the pointer itself can be moved to another variable or constant.  The method of defining a pointer to a constant is illustrated in line 22.  In addition to a pointer to a constant, you can also declare a constant pointer, one that cannot be changed.  Line 23 illustrates this.  Note that neither of these pointers are used in illustrative code.

Either of these constructs can be used to provide additional compile time checking and improve the quality of your code.  If you know a pointer will never be moved due to its nature, you should define it as a constant pointer.  If you know that a value will not be changed, it can be defined as a constant and the compiler will tell you if you ever inadvertently attempt to change it.


A POINTER TO VOID

The pointer to void is actually a part of the ANSI-C standard but is relatively new so it is commented upon here.  A pointer to void can be assigned the value of any other pointer type.  You will notice that the pointer to void named general is assigned an address of an int type in line 15 and the address of a float type in line 20 with no cast and no complaints from the compiler.  This is a relatively new concept in C and C++.  It allows a programmer to define a pointer that can be used to point to many different kinds of things to transfer information around within a program.  A good example is the malloc() function which returns a pointer to void.  This pointer can be assigned to point to any entity, thus transferring the returned pointer to the correct type.

A pointer to void is aligned in memory in such a way that it can be used with any of the simple predefined types available in C++, or in ANSI-C for that matter.  They will also align with any compound types the user can define since compound types are composed of the simpler types.

Be sure to compile and execute this program.


DYNAMIC ALLOCATION AND DEALLOCATION

Examine the program named NEWDEL.CPP for our first example of the new and delete operators.    
1 // NEWDEL.CPP
#include <iostream.h>

struct date {
   int month;
   int day;
   int year;
};

void main()
{
12           int index, *point1, *point2;

14           point1 = &index;
                *point1 = 77;
                point2 = new int;
                *point2 = 173;
                cout << "The values are " << index << "  " <<
                                  *point1 << "  " << *point2 << "\n";
                point1 = new int;
                point2 = point1;
                *point1 = 999;
                cout << "The values are " << index << "  " <<
                                  *point1 << "  " << *point2 << "\n";
                delete point1;

                float *float_point1, *float_point2 = new float;

                float_point1 = new float;
                *float_point2 = 3.14159;
                *float_point1 = 2.4 * (*float_point2);
                delete float_point2;
                delete float_point1;

                date *date_point;

                date_point = new date;
                date_point->month = 10;
40           date_point->day = 18;
41           date_point->year = 1938;
42           cout << date_point->month << "/" << date_point->day << "/" <<
                                            date_point->year << "\n";
                delete date_point;

                char *c_point;

                c_point = new char[37];
                delete [] c_point;
                c_point = new char[sizeof(date) + 133];
                delete [] c_point;
}




// Result of execution
//
// The values are 77  77  173
// The values are 77  999  999
// 10/18/1938

The new and delete operators do dynamic allocation and deallocation in much the same  manner that malloc() and free() do in your old favorite C implementation.

During the design of C++, it was felt that since dynamic allocation and deallocation are such a heavily used part of the C programming language and would also be heavily used in C++, it should be a part of the language, rather than a library add-on. 

The new and delete operators are actually a part of the C++ language and are operators, much like the addition operator or the assignment operator.  They are therefore very efficient, and are very easy to use as we will see in this example program.

Lines 14 and 15 illustrate the use of pointers in the tradition of C and line 16 illustrates the use of the new operator.  This operator requires one modifier which must be a type as illustrated here.  The pointer named point2 is now pointing at the dynamically allocated integer variable which exists on the heap, and can be used in the same way that any dynamically allocated variable is used in ANSI-C.  Line 18 illustrates displaying the value on the monitor which was assigned in line 17.

Line 20 allocates another new variable and line 21 causes point2 to refer to the same dynamically allocated variable as point1 is pointing to.  In this case, the reference to the variable that point2 was previously pointing to has been lost and it can never be used or deallocated.  It is lost on the heap until we return to the operating system when it will be reclaimed for further use, so this is obviously not good practice.  Note that point1 is deallocated with the delete operator in line 25, and point2 can not be deleted since it is now pointing to nothing.  Since the pointer point1 itself is not changed, it is actually still pointing to the original data on the heap.  This data could probably be referred to again using point1, but it would be terrible programming practice since you have no guarantee what the system will do with the pointer or the data.  The data storage is returned to the free list to be allocated in a subsequent call, and will soon be reused in any practical program.

Since the delete operator is defined to do nothing if it is passed a NULL value, it is legal to ask the system to delete the data pointed to by a pointer with the value of NULL, but nothing will actually happen.  It is actually wasted code.  The delete operator can only be used to delete data allocated by a new operator.  If the delete is used with any other kind of data, the operation is undefined and anything can happen.  According to the ANSI standard, even a system crash is a legal result of this illegal operation, and can be defined as such by the compiler writer.

In line 27, we declare some floating point variables.  You will remember that in C++ the variables do not have to be declared at the beginning of a block.  A declaration is an executable statement and can therefore appear anywhere in a list of executable statements.  One of the float variables is allocated within the declaration to illustrate that this can be done.  Some of the same operations are performed on these float type variables as were done on the int types earlier.

Some examples of the use of a structure are given in lines 35 through 41 and should be self explanatory.

Finally, since the new operator requires a type to determine the size of the dynamically allocated block, you may wonder how you can allocate a block of arbitrary size.  This is possible by using the construct illustrated in line 47 where a block of 37 char sized entities, which will be 37 bytes, is allocated.  A block of 133 bytes greater than the size of the date structure is allocated in line 49.  It is therefore clear that the new operator can be used with all of the flexibility of the malloc() function which you are familiar with.  The brackets are required in lines 48 and 50 to tell the compiler that it is deallocating an array.

The standard functions which you have been using in C for dynamic memory management, malloc(), calloc(), and free(), are also available for use in C++ and can be used in the same manner they were used in C.  The new and delete operators should not be intermixed with the older function calls since the results may be unpredictable.  If you are updating code with the older function calls, continue to use them for any additions to the code.  If you are designing and coding a new program you should use the newer constructs because they are a built in part of the language rather than an add on and are therefore more efficient.

Be sure to compile and execute this program.


POINTERS TO FUNCTIONS

Examine the program named FUNCPNT.CPP for an example of using a pointer to a function.  It must be pointed out that there is nothing new here, the pointer to a function is available in ANSI-C as well as in C++ and works in the manner described here for both languages.  It is not regularly used by most C programmers, so it is briefly discussed here as a refresher. 
1 // FUNCPNT.CPP
2 #include <stdio.h>

4 void print_stuff(float data_to_ignore);
5 void print_message(float list_this_data);
6 void print_float(float data_to_print);
7 void (*function_pointer)(float);

void main()
{
11           float pi = 3.14159;
12           float two_pi = 2.0 * pi;

14           print_stuff(pi);
                function_pointer = print_stuff;
                function_pointer(pi);
                function_pointer = print_message;
18           function_pointer(two_pi);
19           function_pointer(13.0);
20           function_pointer = print_float;
                function_pointer(pi);
                print_float(pi);
}


void print_stuff(float data_to_ignore)
{
28           printf("This is the print stuff function.\n");
}


void print_message(float list_this_data)
{
34           printf("The data to be listed is %f\n", list_this_data);
}


void print_float(float data_to_print)
{
40           printf("The data to be printed is %f\n", data_to_print);
}


// Result of execution
//
// This is the print stuff function.
// This is the print stuff function.
// The data to be listed is 6.283180
// The data to be listed is 13.000000
// The data to be printed is 3.141590
// The data to be printed is 3.141590

There is nothing unusual about this program except for the pointer to a function declared in line 7.  This declares a pointer to a function which returns nothing (void) and requires a single formal parameter, a float type variable.  You will notice that all three of the functions declared in lines 4 through 6 fit this profile and are therefore candidates to be called with this pointer.

Observe that in line 14 we call the function print_stuff() with the parameter pi and in line 15 we assign the function pointer named function_pointer the value of print_stuff() and use the function pointer to call the same function again in line 16.  Lines 14 and 16 are therefore identical in what is accomplished because of the pointer assignment in line 15.  In lines 17 through 22, a few more illustrations of the use of the function pointer are given.

Since we assigned the name of a function to a function pointer, and did not get an assignment error, the name of a function must be a pointer to that function.  This is exactly the case.  A function name is a pointer to that function, but it is a pointer constant and cannot be changed.  This is exactly the case we found when we studied arrays in ANSI-C at some point in our C programming background.  An array name is a pointer constant to the first element of the array.

Since the name of the function is a constant pointer to that function, we can assign the name of the function to a function pointer and use the function pointer to call the function.  The only caveat is that the return value and the number and types of parameters must be identical.  Most C and C++ compilers will not, and in fact, cannot warn you of type mismatches between the parameter lists when the assignments are made.  This is because the assignments are done at runtime when no type information is available to the system, rather than at compile time when all type information is available.

Be sure to compile and execute this program.