Search This Blog

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.

Throwback - Compound Types

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.

ENUMERATED TYPES

Examine the file named ENUM.CPP for an example that uses an enumerated type variable. 
// ENUM.CPP
#include <iostream.h>

enum game_result {WIN, LOSE, TIE, CANCEL};

void main()
{
                game_result result;
                enum game_result omit = CANCEL;
                int index;

                for (index = WIN ; index <= CANCEL ; index++)
                {
                                result = (game_result)index;
                                if (result == omit)
                                cout << "The game was cancelled\n";
                                else
                                {
                                cout << "The game was played ";
                                if (result == WIN)
                                                cout << "and we won!";
                                if (result == LOSE)
                                                cout << "and we lost.";
                                cout << "\n";
                                }
                }
}




// Result of execution
//
// The game was played and we won!
// The game was played and we lost.
// The game was played
// The game was cancelled


The numerated type is used in C++ in a very similar way that it was used in ANSI-C.  The keyword enum is not required to be used again when defining a variable of that type, but it can be used if desired.  The name game_result is defined as an enumerated type making the use of the keyword enum optional.  However, it may be clearer for you to use the keyword when defining a variable in the same manner that it is required to be used in C, and you may choose to do so.

The example program uses the keyword enum in line 9, but omits it in line 8 to illustrate to you that it is indeed optional, but that is a trivial difference.  There is a bigger difference in the way an enumerated type is used in C++.  In C, the enumerated type is simply an int type variable, but in C++ it is not an int, but its own type.  Mathematical operations can not be performed on it, nor can an integer be assigned to it.  It cannot be incremented or decremented as it can be in C.  In the example program, an integer is used for the for loop because it can be incremented, then the value of the loop index is assigned to the enumerated variable by using a cast.  The cast is required or a compile error is reported.  The mathematical operations and the increment and decrement operators can be defined for the enumerated type, but they are not automatically available.  Operator overloading will be studied later, and the last sentence will make much more sense at that time.

The remainder of this program should be no problem for you to understand.  After studying it, be sure to compile and execute it and examine the output.


A SIMPLE STRUCTURE

Examine the example program named STRUCTUR.CPP for an illustration using a very simple structure.  
// STRUCTUR.CPP
#include <iostream.h>

struct animal
{
                int weight;
                int feet;
};

void main()
{
                animal dog1, dog2, chicken;
                animal cat1;
                struct animal cat2;

                dog1.weight = 15;
                dog2.weight = 37;
                chicken.weight = 3;

                dog1.feet = 4;
                dog2.feet = 4;
                chicken.feet = 2;

                cout << "The weight of dog1 is " << dog1.weight << "\n";
                cout << "The weight of dog2 is " << dog2.weight << "\n";
                cout << "The weight of chicken is " << chicken.weight << "\n";
}




// Result of execution
//
// The weight of dog1 is 15
// The weight of dog2 is 37
// The weight of chicken is 3


This structure is no different from that used in ANSI-C except for the fact that the keyword struct is not required to be used again when defining a variable of that type.  Lines 11 and 12 illustrate the declaration of variables without the keyword, and line 13 indicates that the keyword struct can be included if desired.  It is up to you to choose which style you prefer to use in your C++ programs.
You should take note of the fact that this is a valid ANSI-C program except for the fact that it uses the stream library, the C++ comments, and the lack of use of the keyword struct in two of the lines.

Once again, be sure to compile and execute this program after studying it carefully, because the next example program is very similar but it introduces a brand new construct not available in standard C, the class.


A VERY SIMPLE CLASS

Examine the example program named CLASS1.CPP for our first example of a class in C++.
// CLASS1.CPP
#include <iostream.h>

class animal
{
                public:
                                int weight;
                                int feet;
};

void main()
{
                animal dog1, dog2, chicken;
                animal cat1;
                class animal cat2;

                dog1.weight = 15;
                dog2.weight = 37;
                chicken.weight = 3;

                dog1.feet = 4;
                dog2.feet = 4;
                chicken.feet = 2;

                cout << "The weight of dog1 is " << dog1.weight << "\n";
                cout << "The weight of dog2 is " << dog2.weight << "\n";
                cout << "The weight of chicken is " << chicken.weight << "\n";
}




// Result of execution
//
// The weight of dog1 is 15
// The weight of dog2 is 37
// The weight of chicken is 3


This is the first class example, but it will not be the last, since the class is the major reason for using C++ over ANSI-C or some other programming language.  You will notice the keyword class used in line 4, in exactly the same way that the keyword struct was used in the last program, and they are in fact very similar constructs.  There are definite differences, as we will see, but for the present time we will be concerned more with their similarities.

The word animal in line 4 is the name of the class, and when we declare variables of this type in lines 12 through 14, we can either omit the keyword class or include it if desired as illustrated in line 14.  In the last program, we declared 5 variables of a structure type, but in this program we declare 5 objects.  They are called objects because they are of a class type.  The differences are subtle, and in this case the differences are negligible, but as we proceed through this tutorial, we will see that the class construct is indeed very important and valuable.  The class was introduced here only to give you a glimpse of what is to come later in this tutorial.

The class is a type which can be used to declare objects in much the same way that a structure is a type that can be used to declare variables.  Your dog named King is a specific instance of the general class of dogs, and in a similar manner, an object is a specific instance of a class.  It would be well to take note of the fact that the class is such a generalized concept that there are libraries of prewritten classes available in the marketplace.  You can purchase classes which perform some generalized operations such as managing stacks, queues, or lists, sorting data, managing windows, etc.  This is because of the generality and flexibility of the class construct.

The new keyword public in line 5, followed by a colon, is necessary in this case because the variables in a class are defaulted to a private type and we could not access them at all without making them public.  Don't worry about this program yet, we will cover all of this in great detail later in this tutorial.

Be sure to compile and run this example program to see that it does what we say it does with your compiler.  Keep in mind that this is your first example of a class and it illustrates essentially nothing concerning the use of this powerful C++ construct.


THE FREE UNION OF C++

Examine the program named UNIONEX.CPP for an example of a free union. 
// UNIONEX.CPP
#include <iostream.h>

struct aircraft
{
                int wingspan;
                int passengers;
                union
                {
                                float fuel_load;     // for fighters
                                float bomb_load;     // for bombers
                                int pallets;         // for transports
                };
} fighter, bomber, transport;

void main()
{
                fighter.wingspan = 40;
                fighter.passengers = 1;
                fighter.fuel_load = 12000.0;
  
                bomber.wingspan = 90;
                bomber.passengers = 12;
                bomber.bomb_load = 14000.0;
  
                transport.wingspan = 106;
                transport.passengers = 4;
                transport.pallets = 42;
  
                transport.fuel_load = 18000.0;
                fighter.pallets = 4;
  
                cout << "The fighter carries "
                              << fighter.pallets << " pallets.\n";
                cout << "The bomber bomb load is " << bomber.bomb_load << "\n";
  
}




// Result of execution
//
// The fighter carries 4 pallets.
// The bomber bomb load is 14000

In ANSI-C, all unions must be named in order to be used, but this is not true in C++.  When using C++ we can use a free union, a union without a name.  The union is embedded within a simple structure and you will notice that there is not a variable name following the declaration of the union in line 11.  In ANSI-C, we would have to name the union and give a triple name (three names dotted together) to access the members.  Since it is a free union, there is no union name, and the variables are accessed with only a doubly dotted name as illustrated in lines 18, 22, 26, and others.

You will recall that a union causes all the data contained within the union to be stored in the same physical memory locations, such that only one variable is actually available at a time.  This is exactly what is happening here.  The variable named fuel_load, bomb_load, and pallets are stored in the same physical memory locations and it is up to the programmer to keep track of which variable is stored there at any given time.  You will notice that the transport is assigned a value for pallets in line 26, then a value for fuel_load in line 28.  When the value for fuel_load is assigned, the value for pallets is corrupted and is no longer available since it was stored where fuel_load is currently stored.  The observant student will notice that this is exactly the way the union is used in ANSI-C except for the way components are named.

The remainder of the program should be easy for you to understand, so after you study and understand it, compile and execute it.


C++ TYPE CONVERSIONS

Examine the program named TYPECONV.CPP for a few examples of type conversions in C++. 
// TYPECONV.CPP
#include <iostream.h>

void main()
{
                int a = 2;
                float x = 17.1, y = 8.95, z;
                char c;

                c = (char)a + (char)x;
                c = (char)(a + (int)x);
                c = (char)(a + x);
                c = a + x;

                z = (float)((int)x * (int)y);
                z = (float)((int)(x * y));
                z = x * y;

                c = char(a) + char(x);
                c = char(a + int(x));
                c = char(a + x);
                c = a + x;

                z = float(int(x) * int(y));
                z = float(int(x * y));
                z = x * y;
}




// Result of execution
//
// (There is no output from this program)


The type conversions are done in C++ in exactly the same manner as they are done in ANSI-C,  but C++ gives you another form for doing the conversions. Lines 10 through 17 of this program use the familiar "cast" form of type conversions used in ANSI-C, and there is nothing new to the experienced C programmer.  You will notice that lines 10 through 13 are actually all identical to each other.  The only difference is that we are coercing the compiler to do the indicated type conversions prior to doing the addition and the assignment in some of the statements.  In line 13, the int type variable will be converted to type float prior to the addition, then the resulting float will be converted to type char prior to being assigned to the variable c.

Additional examples of type coercion are given in lines 15 through 17 and all three of these lines are essentially the same.

The examples given in lines 19 through 26 are unique to C++ and are not valid in ANSI-C.  In these lines the type coercions are written as though they are function calls instead of the more familiar "cast" method as illustrated earlier.  Lines 19 through 26 are identical to lines 10 through 17.

You may find this method of type coercion to be clearer and easier to understand than the "cast" method and in C++ you are free to use either, or to mix them if you so desire, but your code could be very difficult to read if you indescriminantly mix them.

Be sure to compile and execute this example program.