Sunday, April 26, 2020

Throwback - Simple Things in C++

This is a post from one my even older blogs. Migrating it here due to discontinuation of the pltform.


EVEN COMMENTS ARE IMPROVED IN C++

Examine the file named CONCOM.CPP for an example of several new things in C++.  We will take the new constructs one at a time beginning with the comments.
//CONCOM.CPP

#include <iostream.h>      
/* This is the stream definition file */

void print_it(const int data_value);

void main()
{
                const int START = 3;        // The value of START cannot be changed
                const int STOP = 9;         // The value of STOP cannot be changed
                volatile int CENTER = 6;   
/* The value of CENTER may be changed by something external to this program.*/
                int index;                 
/* A normal C variable*/

                for (index = START ; index < STOP ; index++)
                                print_it(index);
}             
/* End of program */


void print_it(const int data_value)
{
                cout << "The value of the index is  " << data_value << "\n";
}




// Result of execution
//
// The value of the index is  3
// The value of the index is  4
// The value of the index is  5
// The value of the index is  6
// The value of the index is  7
// The value of the index is  8

A C++ comment begins with the double slash "//", starts anywhere on a line, and runs to the end of that line where it is automatically terminated.  The old method of comment definition used with ANSI-C can also be used with C++ as illustrated in lines 11 through 14, among other places in this program.  The new method is the preferred method of comment definition because it is impossible to inadvertently comment out several lines of code.  This can be done by forgetting to include the end of comment notation when using the older C method of comment notation.  Good programming practice would be to use the new method for all comments and reserve the old method for use in commenting out a section of code during debugging since the two methods can be nested.

It would be well to caution you at this point however, that you should not use comments when the same sense of program definition can be obtained by using meaningful names for variables, constants, and functions.  The careful selection of variable and function names can make nearly any code self documenting and you should strive to achieve this in your code.


THE KEYWORDS const AND volatile

There are two new keywords used in lines 9 through 11 which were not part of the original K&R definition of C, but are part of the ANSI-C standard.  The keyword const is used to define a constant. 
In line 9 the constant is of type int, it is named START, and is initialized to the value 3.  The compiler will not allow you to accidentally or purposefully change the value of START because it has been declared a constant.  If you had a variable named STARTS in this program, the system would not allow you to slightly misspell STARTS as START and accidentally change it.  The compiler would give you an error message so you could fix the error.  Since it is not permissible to change the value of a constant, it is imperative that you initialize it when it is declared so it will have a useful value.  The compiler does not require you to initialize it however, and will not issue an error message if you do not.

You will note that the keyword const is also used in the function header in line 21 to indicate that the formal parameter named data_value is a constant throughout the function.  Any attempt to assign a new value to this variable will result in a compile error.  This is a small thing you can add to your programs to improve the compilers ability to detect errors for you.

The keyword volatile is also part of the ANSI-C standard but was not included in the original K&R definition of C.  Even though the value of a volatile variable can be changed by you, the programmer, there may be another mechanism by which the value could be changed, such as by a hardware interrupt timer causing the value to be incremented.  The compiler needs to know that this value may be changed by some external force when it optimizes the code.  A study of code optimization methods is very interesting, but beyond the scope of this tutorial.  Note that a constant can also be volatile, which means that you cannot change it, but the system can modify it through some hardware function.

Ignore the output statement given in line 23 for a few minutes.  We will study it in some detail later in this chapter.  If you are experienced in the K&R style of programming, you may find line 5 and 21 a little strange.  This illustrates prototyping and the modern method of function definition as defined by the ANSI-C standard.  Prototyping is optional in C but absolutely required in C++.  For that reason, chapter 4 of this tutorial is devoted entirely to prototyping.

It would be advantageous for you to compile and execute this program with your C++ compiler to see if you get the same result as given in the comments at the end of the listing.  One of the primary purposes of compiling it is to prove that your compiler is loaded and executing properly.


THE SCOPE OPERATOR

The example program named SCOPEOP.CPP illustrates another construct that is new to  C++.  There is no corresponding construct in either K&R or ANSI-C. 
// SCOPEOP.CPP
#include <iostream.h>

int index = 13;

void main()
{
                float index = 3.1415;

                cout << "The local index value is " << index << "\n";
                cout << "The global index value is " << ::index << "\n";

                ::index = index + 7;  // 3 + 7 should result in 10

                cout << "The local index value is " << index << "\n";
                cout << "The global index value is " << ::index << "\n";
}




// Result of execution
//
// The local index value is 3.1415
// The global index value is 13
// The local index value is 3.1415
// The global index value is 10

This allows access to the global variable named index even though there is a local variable of the same name within the main function.  The use of the double colon in front of the variable name, in lines 11, 13, and 16, instructs the system that we are interested in using the global variable named index, defined in line 4, rather than the local variable defined in line 8.

The use of this technique allows access to the global variable for any use.  It could be used in calculations, as a function parameter, or for any other purpose.  It is not really good programming practice to abuse this construct, because it could make the code difficult to read.  It would be best to use a different variable name instead of reusing this name, but the construct is available to you if you find that you need it sometime.

Be sure to compile and execute this program before proceeding on to the next example program where we will discuss the cout operator used in lines 10, 11, 15, and 16.


THE iostream LIBRARY

Examine the example program named MESSAGE.CPP for our first hint of object oriented programming, even though it is a very simple one. 
// MESSAGE.CPP
#include <iostream.h>
#include <string.h>

void main()
{
                int index;
                float distance;
                char letter;
                char name[25];

                index = -23;
                distance = 12.345;
                letter = 'X';
                strcpy(name, "John Doe");

                cout << "The value of index is "    << index    << "\n";
                cout << "The value of distance is " << distance << "\n";
                cout << "The value of letter is "   << letter   << "\n";
                cout << "The value of name is "     << name     << "\n";

                index = 31;
                cout << "The decimal value of index is " << dec << index << "\n";
                cout << "The octal value of index is " << oct << index << "\n";
                cout << "The hex value of index is " << hex << index << "\n";
                cout << "The character letter is " << (char)letter << "\n";

                cout << "Input a decimal value --> ";
                cin  >> index;
                cout << "The hex value of the input is " << index << "\n";
}




// Result of execution
//
// The value of index is -23
// The value of distance is 12.345
// The value of letter is X
// The value of name is John Doe
// The decimal value of index is   31
// The octal value of index is   37
// The hex value of index is   1f
// The character letter is X
// Input a decimal value --> 999
// The hex value of the input is 3e7

In this program, we define a few variables and assign values to them for use in the output statements illustrated in lines 17 through 20, and in lines 23 through 26.  The operator cout is the output function to the standard device, the monitor, but works a little differently from our old familiar printf() function, because we do not have to tell the system what type we are outputting.

C++, like the C language itself, has no input or output operations as part of the language itself, but defines the stream library to add input and output functions in a very elegant manner.  The stream library is included in this program in line 2.

The operator <<, sometimes called the "put to" operator but more properly called the insertion operator, tells the system to output the variable or constant following it, but lets the system decide how to output the data.  In line 17, we first tell the system to output the string, which it does by copying characters to the monitor, then we tell it to output the value of index.  Notice however, that we fail to tell it what the type is or how to output the value.  Since we don't tell the system what the type is, it is up to the system to determine what the type is and to output the value accordingly.  After the system finds the correct type, we also leave it up to the system to use the built in default as to how many characters should be used for this output.  In this case, we find that the system uses exactly as many characters as needed to output the data, with no leading or trailing blanks, which is fine for this output.  Finally, the newline character is output as a single character string, and the line of code is terminated with a semicolon.

When we called the cout output function in line 17, we actually called two different functions because we used it to output two strings and a variable of type int.  This is the first hint at object oriented programming because we simply broadcast a message to the system to print out a value, and let the system find an appropriate function to do so.  We are not required to tell the system exactly how to output the data, we only tell it to output it.  This is a very weak example of object oriented programming, and we will study it in much more depth later in this tutorial.

In line 18, we tell the system to output a different string, followed by a floating point number, and another string of one character, the newline character.  In this case, we told it to output a floating point number without telling it that it was a floating point number, once again letting the system choose the appropriate output means based on its type.  We did lose a bit of control in the transaction, however, because we had no control over how many significant digits to print before or after the decimal point.  We chose to let the system decide how to format the output data.

The variable named letter is of type char, and is assigned the value of the uppercase X in line 14, then printed as a letter in line 19.

Because C++ has several other operators and functions available with streams, you have complete flexibility in the use of the stream output functions.  You should refer to your compiler documentation for details of other available formatting commands.  Because it is expected to be mandated by the upcoming ANSI-C++ standard, the cout and printf() statements can be mixed in any way you desire.  However all compilers do not yet conform to this standard and some use different forms of buffering for the two kinds of output.  This results in scrambled output, but should be easy for the student to repair the output in such a way that only one form is used, either the cout or the printf().


MORE ABOUT THE stream LIBRARY

The stream library was defined for use with C++ in order to add to the execution efficiency of the language.  The printf() function was developed early in the life of the C language and is meant to be all things to all programmers.  As a result, it became a huge function with lots of extra baggage, most of which is used by only a few programmers.  By defining the small special purpose stream library, the designer of C++ allows the programmer to use somewhat limited formatting capabilities, which are still adequate for most programming jobs.  If more elaborate formatting  capabilities are required, the complete printf() library is available within any C++ program, and eventually the two types of outputs can be freely mixed.  If your compiler does not yet support this, it will be up to you to see that only one type is used in any given program.  Although it is not all illustrated here, there is a full set of formatting functions available in the C++ stream library.  Check your compiler documentation for the complete list.

Lines 23 through 26 illustrate some of the additional features of the stream library which can be used to output data in a very flexible yet controlled format.  The value of index is printed out in decimal, octal, and hexadecimal format in lines 23 through 25.  When one of the special stream operators, dec, oct, or hex, is output, all successive output will be in that number base.  Looking ahead to line 30, we find the value of index printed in hex format due to the selection of the hexadecimal base in line 25.  If none of these special stream operators are output, the system defaults to decimal format.


THE cin OPERATOR

In addition to the cout operator, there is a cin operator which is used to read data from the standard input device, usually the keyboard.  The cin operator uses the >> operator, usually called the "get from" operator but properly called the extraction operator.  It has most of the flexibility of the cout operator. 
A brief example of the use of the cin operator is given in lines 28 through 30.  The special stream operators, dec, oct, and hex, also select the number base for the cin stream separately from the cout stream.  If none is specified, the input stream also defaults to decimal.

In addition to the cout operator and the cin operator there is one more standard operator, the cerr, which is used to output to the error handling device.  This device cannot be redirected to a file like the output to the cout can be.  The three operators, cout, cin, and cerr, correspond to the stdout, the stdin, and the stderr stream pointers of the C programming language.  Their use will be illustrated throughout the remainder of this tutorial.

The stream library also has file I/O capability which will be briefly illustrated in the next example program.

Be sure to compile and execute this program before going on to the next one.  Remember that the system will ask you to enter an integer value which will be echoed back to the monitor, but changed to the hexadecimal base.

FILE STREAM OPERATIONS

Examine the example program named FSTREAM.CPP for examples of the use of streams with files. 

// FSTREAM.CPP
#include <iostream.h>
#include <fstream.h>
#include <process.h>

void main()
{
                ifstream infile;
                ofstream outfile;
                ofstream printer;
                char filename[20];

                cout << "Enter the desired file to copy ----> ";

                cin >> filename;

                infile.open(filename, ios::nocreate);
                if (!infile)
                {
                                cout << "Input file cannot be opened.\n";
                                exit(1);
                }
                outfile.open("copy");
                if (!outfile)
                {
                                cout << "Output file cannot be opened.\n";
                                exit(1);
                }

                printer.open("PRN");
                if (!printer)
                {
                                cout << "There is a problem with the printer.\n";
                                exit(1);
                }

                cout << "All three files have been opened.\n";

                char one_char;

                printer << "This is the beginning of the printed copy.\n\n";

                while (infile.get(one_char))
                {
                                outfile.put(one_char);
                                printer.put(one_char);
                }

                printer << "\n\nThis is the end of the printed copy.\n";

                infile.close();
                outfile.close();
                printer.close();
}



// Result of execution
//
// (The input file is copied to the file named "COPY")
// (The input file is printed on the printer

In this program a file is opened for reading, another for writing, and a third stream is opened to the printer to illustrate the semantics of stream operations on a file.  Both input and output files are of type FILE in C programs, but the ifstream type is used for file input and the ofstream is used for output files.  This is illustrated in lines 8 through 10 of this example program.

The only difference between the streams in the last program and the streams in this program is the fact that in the last program, the streams were already opened for us by the system.  You will note that the stream named printer is used in the same way we used the cout operator in the last program.  Finally, because we wish to exercise good programming practice, we close all of the files we have opened prior to ending the program.

This is our first example of object oriented programming because we are actually using objects in this program.  The object named infile is told to open itself in line 17, then it is told to get one character at a time in line 41, and finally it is told to close itself in line 48.  The "dot" notation is used with objects in a similar manner that structures are used in C.  The name of the object is mentioned, followed by a "dot", and followed by the name of the action that the object is to execute.  This terminology will be used in great profusion later in this tutorial, so don't worry about it at this time.  The objects outfile and printer are manipulated in exactly the same manner.

Be sure to compile and execute this program.  When you execute it, it will request a file to be copied.  You can enter the name of any ASCII file that resides in the current directory.


VARIABLE DEFINITIONS

Examine the file named VARDEF.CPP for a few more additions to the C++ language which aid in writing a clear and easy to understand program. 
// VARDEF.CPP
#include <iostream.h>

int index;

void main()
{
                int stuff;
                int &another_stuff = stuff; // A synonym for stuff

                stuff = index + 14;      //index was initialized to zero
                cout << "stuff has the value " << stuff << "\n";
                stuff = 17;
                cout << "another_stuff has the value " << another_stuff << "\n";

                int more_stuff = 13;        //not automatically initialized

                cout << "more_stuff has the value " << more_stuff << "\n";
  
                for (int count = 3 ; count < 8 ; count++)
                {
                                cout << "count has the value " << count << "\n";
                                char count2 = count + 65;
                                cout << "count2 has the value " << count2 << "\n";
                }
  
                static unsigned goofy;      //automatically initialized to zero

                cout << "goofy has the value " << goofy << "\n";
}




// Result of execution
//
// stuff has the value 14
// another_stuff has the value 17
// more_stuff has the value 13
// count has the value 3
// count2 has the value D
// count has the value 4
// count2 has the value E
// count has the value 5
// count2 has the value F
// count has the value 6
// count2 has the value G
// count has the value 7
// count2 has the value H
// goofy has the value 0

In C++, as in ANSI-C, global and static variables are automatically initialized to zero when they are declared.  The variables named index in line 4, and goofy in line 26 are therefore automatically initialized to zero.  Of course, you can still initialize either to some other value if you so desire.  Global variables are sometimes called external since they are external to any functions.

Automatic variables, those declared inside of any function, are not automatically initialized but will contain the value that happens to be in the location where they are defined, which must be considered a garbage value.  The variable named stuff in line 8, therefore does not contain a valid value, but some garbage value which should not be used for any meaningful purpose.  In line 11, it is assigned a value based on the initialized value of index and it is then displayed on the monitor for your examination.


THE REFERENCE VARIABLE

Notice the ampersand in line 9.  This defines another_stuff as a reference variable which is a new addition to C++.  The reference variable should not be used very often, if ever, in this context, but this is a very simple example used to introduce the reference variable and discuss its use and operation.  The reference variable is not quite the same as any other variable used in C because it operates like a self dereferencing pointer.  Following its initialization, the reference variable becomes a synonym for the variable stuff, and changing the value of stuff will change the value of another_stuff because they are both actually referring to the same variable.  The synonym can be used to access the value of the variable for any legal purpose in the language.  It should be pointed out that a reference variable must be initialized to reference some other variable when it is declared or the compiler will respond with an error.  Following initialization, the reference variable cannot be changed to refer to a different actual variable.

The use of the reference variable in this way can lead to very confusing code, but it has another use where it can make the code very clear and easy to understand.  We will study the proper use of the reference variable in chapter 4 of this tutorial.


DEFINITIONS ARE EXECUTABLE STATEMENTS

Coming from your background of C, you will find the statement in line 16 very strange, but this is legal in C++.  Anyplace it is legal to put an executable statement, it is also legal to declare a new variable because a data declaration is defined as an executable statement in C++.  In this case, we define the new variable named more_stuff and initialize it to the value of 13.  It has a scope from the point where it is defined to the end of the block in which it is defined, so it is valid throughout the remainder of the main program.  The variable named goofy is declared even later in line 26.

It is significant that the variable is declared near its point of usage.  This makes it easier to see just what the variable is used for, since it has a much more restricted scope of validity.  When you are debugging a program, it is convenient if the variable declaration is located in close proximity to where you are debugging the code.


WHAT ABOUT definition AND declaration?

The words definition and declaration refer to two different things in C++, and in ANSI-C also for that matter.  They really are different and have different meanings, so we should spend a little time defining exactly what the words mean in C++.  A declaration provides information to the compiler about the characteristics of something such as a type or a function but it doesn't actually define any code to be used in the executable program.  A definition, on the other hand, actually defines something that will exist in the executable program, either some useful variables, or some executable code, and you are required to have one and only one definition of each entity in the program.  In short, a declaration introduces a name into the program and a definition introduces some code and requires memory space to store something.

If we declare a struct, we are only declaring a pattern to tell the compiler how to store data later when we define one or more variables of that type.  But when we define some variables of that type, we are actually declaring their names for use by the compiler, and defining a storage location to store the values of the variables.  Therefore, when we define a variable, we are actually declaring it and defining it at the same time.

C permits multiple definitions of a variable in any given namespace, provided the definitions are the same and it generates only a single variable for the multiple definitions.  C++, however, does not permit redefinition of a variable or any other entity for a very definite reason that we will discuss later.

We will refer to these definitions many times throughout the course of this tutorial so if this is not clear now, it will clear up later.


A BETTER for LOOP

Take careful notice of the for loop defined in line 20.  This loop is a little clearer than the for loop that is available in ANSI-C, because the loop index is defined in the for loop itself.  The scope of this loop index is from its declaration to the end of the enclosing block.  In this case its scope extends to line 29 since the closing brace in line 29 corresponds to the most recent opening brace prior to the declaration of the variable.  Since the variable is still available, it can be used for another loop index or for any other purpose which an integer type variable can legally be used for.  The variable named count2 is declared and initialized during each pass through the loop because it is declared within the block controlled by the for loop.  Its scope is only the extent of the loop so that it is automatically deallocated each time the loop is completed.  It is therefore declared, initialized, used and deallocated five times, once for each pass through the loop.

You will notice that the variable count2 is assigned a numerical value in line 22 but when it is printed out, a character value is actually output.  This is because C++ (version 2.0 and later) is careful to use the correct type for output.

Finally, as mentioned earlier, the static variable named goofy is declared and automatically initialized to zero in line 26.  Its scope is from the point of its declaration to the end of the block in which it is declared, line 29.

Be sure to compile and execute this program.


OPERATOR PRECEDENCE

Operator precedence is identical to that defined for ANSI-C so no attempt will be made here to define it.    Some of the operators act slightly different when overloaded than the way they operate with elements of the predefined language.

No comments:

Post a Comment