These notes were prepared by Petros Komodromos.
Topics
- Course goals & content, references, recitations
- Compilation
- Debugging
- Makefiles
- Concurrent Versions System (CVS)
- Introduction to C++
- Data Types
- Variable Declarations and Definitions
- Operators
- Expressions and Statements
- Input/Output Operators
- Preprocessor directives
- Header files
- Control Structures
1. Course Goals and Content
- C++:
Procedural and Object Oriented Programming
Data structures
Software design and implementation
-
Algorithms:
sorting (insertion, selection, mergesort, quicksort, shellsort, hashing)
searching (linear search, binary search, binary search trees)
-
Java®:
Object Oriented Programming (OOP) using Java®, Java® application and applets,
Graphical User Interfaces, Graphics using Java®
-
Advanced Topics:
Geometric algorithms, Java®3D, Database design using JDBC, etc.
- Term Project
Textbooks - References
-
C++
- C++ Primer. Lippman and Lajoie. 3rd edition (required).
- C++ How to program. Deitel & Deitel. 3rd edition.
- The C++ programming language. Bjarne Stroustrup. 3rd edition.
- On to C++. H. Winston. 2nd edition.
- Object Oriented Programming in C++. Johnsonbaugh & Kalin.
-
C++ FAQ. Cline/Lomow.
-
Algorithms
- Algorithms in C++. Sedgewick (required).
- Algorithms, Data Structures and Problem Solving with C++. Weiss.
-
Introduction to Algorithms. Cormen, Leiserson, and Rivest.
-
Java®
- The Java® Tutorial. Mary Campione and Kathy Walrath (required).
- Core Java®. Gary Cornell and Cay Horstmann. 2nd edition.
- The Java® programming language. Ken Arnold and James Gosling. 2nd edition.
- Java®: How to program. Deitel & Deitel. 2nd edition.
Problem Sets
You need to follow the instructions that are provided with each problem set, concerning what you must submit. In all problem sets you must both electronically turnin the source code files, and submit hardcopies of all completed, or modified, source code files. Sometimes you may also need to provide screen dumps of the window with the output results from the execution of your programs.
The problem statement and the provided source code files can be obtained using CVS, which is a version control system (covered later in this recitation). Whenever source code files are provided, the following naming convention is used:
For each question there is a ps<number_of_problem_set>_<number_of_question>.C which you may need to use, e.g. ps3_2.C for question 2 of problem set 3.
In some cases a makefile (named make<number_of_problem_set>) is provided, which you may use to compile and link your code.
Please comment your code to make it more readable whenever you think it would be helpful for someone else to understand what and how you do it (i.e. for the graders). Comments may be incorporated in your code by either enclosing them between /* and */, or, by putting them on the right side of two division symbols //.
It is also very useful for both yourselves, and the graders, to indent your code in order to emphasize loops and different parts of your code. You should indent your code so that different blocks start in different columns making it less obscure and difficult to understand.
Please, do not make any other changes to the provided code, except those that you are asked to make.
You can print a file in a compact form (saving some paper) using the following command so as to have the name of the file and the time and date printed on the hardcopy.
athena% enscript -2Gr -P<printer name> <filename>
Whenever necessary, you can dump an X window directly to a printer using the following command and clicking on the window you want to print.
athena% xdpr -P<printer name>
Homework that is turned in late will be penalized as follows:
- If turned in one day late i.e. by 2:30 p.m. on the day after the due date, the penalty is 10% of the overall score (i.e. 10 points off). You can turn in the problem set solution only once. The first hardcopy you will turn in will be the one to be graded, i.e. if you plan to submit it late you should not submit a solution on the due date, as well, but only the late one the day after the due date.
- If turned in more than one day late, the penalty is 100% i.e. no credit will be awarded. No exceptions!
Please, always staple your hardcopies together and write clearly on the first page your name and username. Also, type within a comment at the top of each file you submit the following information:
- your first and last name
- the problem set number, and
- the question number
2. Compilation
After writing the source code of a program, e.g. using an editor such as emacs, you must compile it, which translates the source code into machine instructions. For the C++ programs you need to use the GNU g++ compiler. To be able to use this compiler, you need to add its locker using the following command (on the Athena prompt):
% add -f gnu
You can customize your account, using a dotfile, so that it will automatically add the gnu locker at start-up. In the file .environment you need to add the following line:
add -f gnu
The add command attaches the specified locker to your workstation and adds it to your path. Dotfiles, such as .environment and .cshrc.mine, can be used to set environment variables, shell aliases and attach lockers in order to get the desired working environment. In the .environment dotfile you may also put the following lines to avoid typing them every time you log in:
add infoagents
add 1.124
setenv CVSROOT /afs/athena.mit.edu/course/1/1.124/src
You can check if you properly use the GNU compiler by giving the following commands:
% which g++ which should give you something like:
/mit/gnu/arch/sun4x_55/bin/g++ or /mit/gnu/arch/sgi_53/bin/g++
Then, you can use the GNU compiler to compile a C++ source code file. For example, to compile and link the source code file ps0_1.C, which is provided in PS0, you can use the following command:
% g++ ps0_1 .C
Then, you can run the generated executable file, which is by default named a.out, by typing its name at the Athena prompt.
To give a specific name to the generated executable the -o option must be used:
% g++ ps0_1 .C -o ps0_1
Then, the generated executable file is named ps0_1
Sometimes, you may need to include an external library, e.g. the math library, using the -l option as follows, and the name of the external library (below the math library is included using m after -l) you want to include (in addition to including its header file in your files):
% g++ ps0_1 .C -o ps0_1 -lm
In some cases that you only need to compile a file without linking, i.e. to generate only the corresponding object file (machine language versions of the source code) and not the executable, you need to use the -c option. (In the following example a ps0_1.o will be generated)
% g++ -c ps0_1 .C
You also need to use the flags -ansi -pedantic to enforce the rules of ANSI Standard C++. In addition, it is useful to use the -Wall option to get all warnings, e.g.:
% g++ -ansi -pedantic -Wall ps0_1 .C -o ps0_1 -lm
It is more convenient to set an alias (e.g. c++), instead of typing all this every time. In particular you can add in your .cshrc.mine dotfile the following:
alias "c++" "g++ -ansi -pedantic -Wall -lm"
and then simply use the following command to compile and link a program:
% c++ ps0_1 .C -o ps0_1
In addition, you can use makefiles to help you automate the compilation and linking of your programs. The following command creates the target_filename according to the instructions provided in the makefile make0a.
% gmake -f makePS0a ps0_1
Eventually you must learn to use makefiles since they are extremely useful for the development of large programs with several different source code files.
Although you may work at any machine and using any compiler you want, you need to make sure that your code compiles properly using the GNU compiler on an Athena workstations. The graders will be using Athena workstations and the GNU compiler to check your solutions.
3. Debugging
Using a debugger can help you find logical and difficult to detect errors in your code much faster and easier. A debugger can be used to step through the program, line by line and examine the program variables while executing it. Therefore, you need to first correct all syntactical errors, using the messages from the compiler and then execute the program using the debugger to detect potential logical errors. Typically, you compile the program, identify errors using a debugger, correct it in emacs, compile and debug again, and repeat as often as necessary.
The debugger allows you to examine in detail what is happening during a program execution, or when it crashes due to a run-time error. In order to be able to use a debugger, such as gdb and ddd, you must first compile and link your code with the flag -g. e.g.:
% g++ -g ps0_1 .C -o ps0_1
-
gdb debugger:
The g++ -g ps0_1 .C -o ps0_1 command generates an executable filename that can be checked with gdb, e.g. using the following command for the program compiled above:
% gdb ps0_1
You can find more information about GDB at Debugging with GDB - The GNU Source-Level Debugger.
-
ddd debugger:
A user friendlier debugger, available on athena, is the Data Display Debugger (ddd), which uses the gdb for its operations. The ddd program is available in the outland locker. Since we also need the g++ compiler from the gnu locker for the compilation you need to type:
% add outland
% add gnu
To invoke the ddd debugger with your executable program, e.g. for ps0_1, please, type:
% ddd ps0_1 &
You can see that three windows pop up:
- The main window, which has three main parts.
- the top panel with the menu bar and the tool bar
- the middle panel with your source code, which is a read only panel code here.
- the bottom panel with the (gdb) prompt, which is the debugging console
- Debugger command tool window, titled ddd
- A tip window, which often provides useful debugging tips. You can close this window.
In general, the debugging cycle involves stepping through your program carefully once it compiles and seems to run. In particular, step into each function that you wrote; step over, e.g. using the ’Next’ button, system functions. At each step, ’Print’ the variable value(s) computed and check them for reasonableness. Keep going until you find logical errors. Then, make the proper correction, using the editor, recompile and do this again.
The execution of the program can be controlled from the ddd command tool which has the Run, Interrupt, Step, Next etc. buttons.
-
To start debugging:
- Find the first executable (non-declaration) line in the program
- Place the cursor on that line (mouse click)
- Then click on the ’Break’ button in the tool panel at the top of the main window. This will set a breakpoint at that line. A breakpoint stops program execution. You should see a stop symbol at that line.
- Click on ’Run’ button in the ddd window.
- This starts program execution, which will run until the next breakpoint or the end. The program will run to the first breakpoint and halt.
- To step through the program:
You can step through the program using the buttons on the ddd window. A green arrow will indicate the current program statement being executed in the source code panel.
- clicking on the ’Next’ button steps over function calls and goes to the next line of the current function
- clicking on the ’Step’ button steps into any function call on the current line
- clicking on 'Finish’ will finish executing the current function. You can use ’Finish’ to come out of any system source code (cout for example) you ’Step’ into
-
Looking at variable values:
- To examine normal variables you can:
- move the mouse on top of a variable and a pop up box will show the variable’s value
- click on the variable so that it becomes highlighted, and then click on the ’Print’ button in the toolbar. Then, the variable's value will appear in the console
-
To examine arrays:
- moving the cursor on top of an array, either the memory address of the first element is shown if values have not been set, or if values have been assigned to the array, the array values are shown
- highlighting the array name the array name will appear in the text box present in the tool bar. If ’Print’ is clicked now, the memory address is printed in the console.
- adding a * before the array name and clicking ’Print’ will print the value stored in the first element of the array. If you wish to see all the elements in the array, replace ’*arrayName’ by ’*arrayName@arraySize’ in the text box and then click ’Print’.
- To display a variable:
You can continuously see the values stored in a variable, by displaying it instead of printing it. The displayed variable will be shown in a new panel which will pop up above the source code panel. As you step through the program, any changes to the variable’s value will be shown there. You can display a variable by:
- highlighting a variable and clicking on the ’Display’ button on the tool bar. Its value is updated every time it changes.
- To undisplay a variable.
- Right click on the variable’s box and choose ’undisplay’.
-
To display values of all local variables in the current function:
- choose "Display local variables" from the ’Data’ tab in the menu bar of the main window.
- choose "Display local variables" from the ’Data’ tab in the menu bar of the main window.
-
To display the arguments passed to the current function:
- choose "Display function arguments" from the ’Data’ tab in the menu bar of the main window.
- choose "Display function arguments" from the ’Data’ tab in the menu bar of the main window.
-
To input and output:
- Input and output is done through the debug console.
- cout line usually found before cin will display a prompt
- stepping to a cin enter your input in the bottom window in the blank line at the bottom
-
Resources:
You can learn more about ddd from the DataDisplayDebugger web-page.
You can look at the ddd manual as well.
4. Use of makefiles
In some cases a makefile (named make<number_of_problem>) will be provided, and you may use it to compile and link your code. Makefiles are used to automate the compilation and linking of programs. To compile and link a specific program, assuming that a proper makefile is available, the following command is used:
% gmake -f make_file_name <program_name>
You do not have to use the provided makefiles, but you can use instead your own makefiles, or any of the makefiles you have seen in the lectures or anywhere else. You need to turnin the makefile that you use to compile your files on athena (either the ones you got using CVS or your own) Learning to use makefiles will help you when you start writing and compiling larger programs with several files, which makes the use of makefiles necessary.
In problem set # 0, a simple makefile is provided for you, called makePS0a, which you may use to compile and link your code. There is also a more advanced makefile named makePS0b which you can use.
e.g. athena% gmake -f makePS0a ps0_1
Executing the above command creates the target filename ps0_1, according to the instructions provided in the makefile makePS0a.
5. Concurrent Version Control (CVS)
For the development of large software packages and programs it is useful to use a control system for modifications and revisions. Although it may not seem very useful for the development of small simple programs (like your first homework problems) it would be very useful for your project, and you will benefit from getting used to using it. Therefore, it would be beneficiary for you to get used to using such a revision control system as CVS (Concurrent Versions System). You may obtain more information on CVS from the man command (% man cvs) and from the following URLs:
- cvs - Concurrent Versions System
- Concurrent Versions System - Tutorials
- CVS Index
- Concurrent Versions System - The Open Standard for Version Control
The provided source code files are in the directory /mit/1.124/Problems/<Problem set number> from where you can copy them using CVS to your directory, and, make the necessary additions and/or modifications. To use CVS to check out the problem sets for the 1.124 you should first set the environment variable CVSROOT as below: (you can also put it in your .environment dotfile)
% setenv CVSROOT /afs/athena.mit.edu/course/1/1.124/src
then you can use the command:
% cvs co Problems/PS<Problem set number>
or, using the alias defined in 1.124/src/CVSROOT/modules
% cvs co OOP_PS<Problem set number>
6. Introduction to C++
C++ is both a procedural oriented programming language and an object oriented programming (OOP) language, since it allows you to organize your program not only around functions (procedures), but also and most commonly used, around data.
A procedural language is based on a list of instructions (statements) organized in procedures (functions) and emphasizing the computations to be performed. Languages such as Fortran and C are procedural languages. C++ which is based on C, has many additional features that enable object oriented programming, where emphasis is given on the data and their behavior. Java® is a pure object oriented language.
The additional features and advantages of C++ are the following:
Classes allow the programmer to create her/his own data types extending the capabilities of the language according to the physical world problems. Classes are similar with the data structures in C, which are also available in C++. However, within a class both data variables and member functions that are used to work with the data variables can be provided.
class Complex
{
public:
double real;
double imaginary;
};
main()
{
Complex x;
x.real =15.5;
x.imaginary = 2.5;
}
There are many additional features, like inheritance and virtual functions, added to C++ which are related with the classes and provide simple ways to handle objects and develop programs in an object oriented programming style.
C++ allows reusability since a class which has been written, debugged and checked can be distributed to other programers and with minimal effort be incorporated in several programming packages.
C++ supports function overloading which allows the use of functions with the same name, as long as they have different signature. The signature of a function is considered its name and the number and type of its arguments. e.g.:
int min(int x, int y) { }
double min(double x, double y) { }
In addition, virtual functions and polymorphism allows the dynamic binding on functions during run-time instead of static binding during compilation.
C++ allows operator overloading, i.e. to use operators with user defined data types according to a specified function associated with the specific operator. For example, we are able to add two complex numbers which are a user defined data type, as long as we provide the necessary functions for operator overloading.
main()
{
Complex x,y,z;
........
z = x + y ;
}
In C++, there are two ways to comment, // (which comments everything until the end of line), and /* */ (which comments everything between /* and */.
Two latest features of C++ are the templates and the exception handling. The templates allow us to parameterize the types within a function or a class providing a general definition that can be used for many different purposes. The exception handling mechanism provides a mechanism to respond to and handle run time errors (like division by zero, exhaustion of memory, etc.).
7. Data Types
- Boolean: bool
- Character and very small integers: char
- Integers: short int, int, long int (short, int, long)
- Floating (single, double and extended precision): float, double, long double
The bool data type can be assigned the values true and false, which correspond to 1 and 0, respectively.
A char can be used both as a character and as an integer, depending on how it is used. Therefore, char, short, int, and long are all called integral data types.
There are also unsigned versions of integer types, which allow the increase of the range of the larger number that can be stored with them, by not using any bit for the sign: unsigned char, unsigned short int (unsigned short), unsigned int, unsigned long int (unsigned long).
The reason of using different data types is mainly for memory efficiency, since we can use the data type which correspond to our needs avoiding useless waste of memory. The proper data type must be selected and used based on the expected requirements during the program’s execution.
To refer to particular variables that correspond to a particular chunk of memory in C++ (and in any other programming language) we have to use identifiers (variable names). The variable names in C++ are case sensitive as they are in C, must begin with a letter or an underscore (_), and should not be a reserved keyword. You should use reasonable variable names that provide some meaning to the reader of your code.
Using the above keywords we can declare the data type of a variable giving to the compiler information about the necessary amount of memory required to store (i.e. the memory that is required to be allocated and reserved for) the variable and which is machine dependent, e.g.:
- int i, j ;
- double x,y,z;
- long int k;
- unsigned short int i;
The const type modifier (or qualifier) defines a variable as a symbolic constant and does not allow any change of its value. Therefore, a const variable must be initialized when defined, since any attempt to change its value results in a compile-time error.
const int i=10;
Note the different meaning of the following declarations:
const int *p: Pointer to a constant integer
int *const p: Constant pointer to an integer
const int *const p: Constant pointer to a constant integer
8. Variable Declarations and Definitions
Before using any variable we have to declare it, i.e. inform the C++ compiler what is the data type of the variable. Every variable has a certain data type that determines the storage requirements and the operations that can be performed on it. In C++, as in C, we can combine several separate variable declarations into one declaration, as long as each variable is of the same data type, e.g.:
<data_type1> <variable1_name>;
<data_type2> <variable2_name> = <initial_value>, <variable3_name>;< /EM >
int a, b, c;
double x,y ;
float z ;
The above declarations are also definitions. A declaration simply informs the compiler that the variable exists, its data type and that it is defined somewhere else in the program. C++ allows to have many declarations of the same variable as long as they are consistent. The definition of a variable defines the variable’s name, its data type and may also initialize the variable. Defining a variable informs the compiler about the variable's data type so as to reserve the proper amount of memory to store values for that variable. The difference is that the definition reserves memory for the variable. Therefore, there must be only one definition of a variable in a program. A declaration is also a definition if it also sets aside memory at compile time.
For example, the following statement is a declaration because it informs the compiler that an external global variable will be used, but no memory is allocated for that variable. The memory is allocated at the definition of the variable.
extern int x_limit ; // declaration
We can also initialize a variables in its definition, assigning an initial value and this is called initialization. When a value is assigned to an already defined variable this is called assignment :
int a, b, c; // definitions
double x = 3.4, y(4.);< /EM > // definitions and initializations
float z = 9.9;< /EM > // definition and initialization
c = 10; < /EM > // assignment
9. Operators
You will often need to use operators which perform a specified action on their operands. Most operators are binary, i.e. they have two operands, e.g. a+b. The operators in C++ are the same as the ones used in C.
These are:
- the arithmetic operators of C++ are the +, -, *, / and %
- the assignment operator is the =
- the shorthand (abbreviated) assignment operators: += , -= , *= and /=
- the (unary) postfix and prefix increment/decrement operators ++ and --
- the relational operators are: > , < , >= , and <= (used to compare two expressions)
- the equality operators are: == and != (used to check two expressions for equality)
- the logical operators are the: && , || , and !
An assignment expression has the value that is assigned to the variable on the LHS, and, therefore, several variables can be assigned the same value in one statement,
e.g.: x = y = z = 100;
The RHS of logical operators is executed only if its necessary for the decision that must be taken. e.g. if the LHS of a ‘logical and’ (&&) is false, there is no reason to examine its RHS.
In addition, in C++ it is allowed to create new definitions for operators applied to user defined data types.
main()
{
Point x,y,z;
........
z = x + y ;
}
10. Expressions and Statements
Each C++ program must contain a function named main(), as in C. The execution of a C++ program begins from the first statement of main and finishes when the last statement of main is executed (e.g. when the last curly brace of main is reached).
An action in C++ is referred as an expression, while an expression terminated by a semicolon is called a statement. More than one statements enclosed in a pair of curly braces is called a compound statement.
Precedence and associativity: The sequence (order) with which individual components of an expression in C++ are executed is based as in C on the order of precedence. When the operators have the same precedence the associativity defines the order of execution. A table with the precedence and associativity of the C++ operators is provided. Similar tables you can find in any C++ textbook.
e.g. a = b += 5 + 3 / 2 - 5 + 17 / 2 / 3
(order): (8) (7) (4) (1) (5) (6) (2) (3)
The following table provides the precedence and associativity of the C++ operators with the highest precedence being the operator :: having precedence level equal to 1.
PRECEDENCE | ASSOCIATIVITY | OPERATOR | FUNCTION |
---|---|---|---|
1 | right | :: | global scope (unary) |
1 | left | :: | class scope (binary) |
2 | left | -> , . | member selectors |
2 | left | [] | array index |
2 | left | () | function call |
2 | left | () | type construction |
3 | right | sizeof | size in bytes |
3 | right | ++ , -- | increment, decrement |
3 | right | ~ | bitwise NOT |
3 | right | ! | logical NOT |
3 | right | + , - | uniary minus, plus |
3 | right | * , & | dereference, address-of |
3 | right | () | type conversion (cast) |
3 | right | new , delete | free store management |
4 | left | ->* , .* | member pointer selectors |
5 | left | * , / , % | multiplicative operators |
6 | left | + , - | arithmetic operators |
7 | left | << , >> | bitwise shift |
8 | left | < , <= , > , >= | relational operators |
9 | left | == , != | equality, inequality |
10 | left | & | bitwise AND |
11 | left | ^ | bitwise XOR |
12 | left | | | bitwise OR |
13 | left | && | logical AND |
14 | left | || | logical OR |
15 | left | ?: | arithmetic if |
16 | right | = , *= , /= , %= , += , -= <<= , >>= , &= , |= , ^= | assignment operators |
17 | left | , | comma operator |
Conversions: C++ defines a set of standard conversions, implicit type conversions, that are used in arithmetic conversions, assignments using different data types, and passing arguments to a function of different data types than the function parameters. In particular, when we have such mixed expressions the compiler makes some standard conversions, e.g. in binary operations the lower data type is promoted to the higher one (which dominates), so as to avoid losing information.
The following order is used:
bool, char, short int < int < long int < float < double < long double
bool, char and short int are always converted to int whenever they appear in any expression, i.e. before performing any operation on them. A bool is promoted to int, getting the value 1 or 0, depending on its value (true or false, respectively). When a number is converted to a type bool all values other than zero are converted to true and a zero value is converted to false. Integer constants (e.g. 17) are considered int, and, floating point constants (e.g. 4.53) are considered double. We can use L after an integer and a floating point constant to define that should be considered as a long int, and, a long double, respectively.
In C++, we can also define rules of conversions to be used with operators applied on user-defined data types.
We can also explicitly define type conversions using casting to force the explicit conversion from one data type to another.
(dataType) variableOrExpression ; and dataType (variableOrExpression) ;
Another way do an explicit conversion, i.e. to cast a data type constant or variable to another data type, is using the keyword static_cast followed by a data type name surrounded by angle brackets and the certain variable or constant to cast within parentheses, e.g.
static_cast <dataType> (variableOrExpression)
static_cast <float> (5) / 3 // gives 1.66667
/* Example: Mixed Expressions - Precedence - Associativity - Casting */
#include <iostream.h>main()
{
int i=4 ;
float f = 2.5 ;
double d = 3;
cout << "\n i / 5 * f = " // Mixed Expressions
<< i / 5 * f << endl ;
cout << "\n 'a' = " << 'a' << endl ;
cout << "\n 'a' - 1 = " // Mixed Expressions
<< 'a' - 1 << endl ;
cout << "\n 'f' - 'd' = " // Mixed Expressions
<< 'f' - 'd' << endl ;
cout << "\n f + 5 * d = " // Precedence
<< f + 5 * d << endl ;
cout << "\n f * 2 * 2.5 = " // Associativity
<< f * 2 * 2.5 << endl ;
cout << "\n (float) i / 5 * f = " // Casting
<< (float) i / 5 * f << endl ;
cout << "\n float i / 5 * f = " // Casting
<< float (i) / 5 * f << endl ;
cout << "\n static_cast <float> (5) / 3 = " // Casting
<< static_cast <float> (5) / 3 << endl;
}
Results
i / 5 * f = 0 float 0.0
'a' = a character
'a' - 1 = 96 int 96
'f' - 'd' = 2 int 2
f + 5 * d = 17.5 double 17.5
f * 2 * 2.5 = 12.5 double 12.5
(float) i / 5 * f = 2 float 2
float(i) / 5 * f = 2 float 2
static_cast <float> (5) / 3 = 1.66667
11. Input/Output Operators
In C++ the predefined objects cin, cout and cerr are available for input and output operations. The predefined object cin refers to the standard input (which is by default the keyboard), cout and cerr refer to the standard output and the standard error, respectively (which are both by default the display). These defaults can be changed using redirection while executing the program.
The output operator (<<) (known as insertion operator) directs (display) output information on your standard output (screen), e.g.
cout << "\n x = " << x << endl ;
"\n" represents a new line character, while endl inserts a new line and flushes the output buffer. The operating system buffers the output to the display characters and prints them out in a batch to minimize I/O overhead. This may lead to wrong indications of where the error may be if we consider the printed out information without flushing the buffer.
We may have several output operators in the same output statement
Similarly, you can use the input operator (>>) to obtain (read) input values from the standard input (keyboard). e.g.:
cout << "\n x = " ;
cin >> x ; cin >> y >> z;
The standard input-output library (iostream.h) must be included using a preprocessor directive, in order to be able to use the input and output operators, as well as the manipulators without arguments such as endl, flush, hex, oct, etc.
Certain options may be specified when using the output stream operator to select the way that the output should look. The precision can be set using a iostream manipulator, the setprecision(number_of_digits), while the setiosflags(options separated by |) can be used e.g. to specify whether the decimal point or tailing zeros should be shown. The setw() specifies the field width in which the next value should be printed out. It is the only manipulator that does not apply to all subsequent input or output, but becomes zero as soon as something is printed. The setfill(c) makes c the fill character. To use these parameterized stream manipulators (i.e. with arguments) we need to include the iomanip.h header file. e.g.:
cout << setprecision(2) << setiosflags(ios::fixed | ios::showpoint)
<< "\n\n 3. = " << 3. << "\t 0.333333 = " << 0.333333 << endl;
(will give: 3.=3.00 0.333333 = 0.33)
We can also redirect the input from the keyboard to a file and the output to another file:
athena% executable_file_name < input_file_name > output_file_name
Finally, there is a standard error stream cerr which is used to display error messages
cerr << "\n Not proper values were provided!" ;
The following member functions can be invoked by the input stream, cin: cin.good() returns true if everything is ok;, cin.eof() return true if EOF is reached; cin.fail() returns true if a format error has occurred.
The C input/output functionsscanf()/printf() can also be used, since C is a subset of C++. In that case the stdio.h header file (which contains their prototypes) must be included. Then, the buffer can explicitly be flushed using "fflush(stdout);".
However, when both C input/output functions and C++ input and output operators are used, you need to provide the following function call before doing any input or output, to avoid problems:
ios::sync_with_stdio();
12. Preprocessor Directives
The preprocessing takes place prior to the actual compilation. The preprocessor searches all files that are to be compiled and takes action according to the preprocessor directives. The preprocessor directives are the lines which begin with # (usually placed at the top of the source code file).
An include preprocessor directive results in the substitution of it, with the contents of the indicated file. i.e. the following include preprocessor directive:
#include <file.h>
is equivalent to typing the contents of the included file at that point.
There are two variations of the include preprocessor directive:
#include <file.h> or #include "file.h"
The difference is that in the first case the preprocessor searches for the included file in the standard include directory, while in the second case it searches in the current directory. The latter case is usually used for the user written functions.
Another preprocessor directive is the #define which it can be used to associate a token string with an identifier, e.g.
#define PI 3.1415926
The preprocessor will replace PI wherever it appears in a file with the provided token. After the preprocessing finishes the compilation starts, in which the token is treated as a floating point constant.
Other preprocessor directives are the following: #ifdef, #ifndef, and, #endif They can be used for conditional compilation.
e.g. the ...... statements will be skipped if _MY_HEADER_H has already been defined.
#ifndef _MY_HEADER_H
#define _MY_HEADER_H
........
#endif
Also, while compiling a program we can define a preprocessor constant on the command line using the -D option followed by the name of the preprocessor constant and therefore certain parts of the code can be selectively excluded.
e.g. compiling the file with -DDEBUG_MODE option will consider the cout statement:
#ifdef DEBUG_MODE
cout << "\n testing debug mode \n" << endl;
#endif
The define directive can also be used to specify macro substitutions with variable parameters. e.g. having defined the following macro using:
#define mult(x,y) (x*y)
then, the following statement: product = 267.2 + mult(25.7, 33.6)
will be replaced during preprocessing by: product = 267.2 + (25.7 * 33.6)
13. Header Files
To be able to use the input and output operators you must first include the standard input-output library (iostream.h) using a preprocessor directive:
#include <iostream.h>
When the C input/output functions scanf()/printf() are used the stdio.h header file (which contains their prototypes) must be included instead.
Similarly to be able to use any other standard library function you need to include its header file which contains all necessary declarations.
e.g. to be able to use the math function, such as sqrt() you need to include the math.h header file using the following command:
#include <math.h>
When the header file that you include is in the current directory, e.g. a header file that you wrote, then you should use double quotes, instead of
brackets, e.g.:
#include "myheader.h"
According to the new ANSI/ISO standard, which however is not followed by all available compilers yet, the iostream header file can be included using:
#include <iostream>
using namespace std;
int main()
{
std::cout << "\n testing:
pi = " << 3.1415 << endl;
}
Header files are very useful to provide declarations (e.g. for global variables and functions) in order to avoid incompatible declarations which may happen when multiple declarations are provided in several source-code files. In addition, any changes to a declaration would require only a single local modification instead of having to update all appearances of the declarations. A header file should never contain definitions, (unless its a definition of an inline function).
14. Control Structures
Control statements are used to control the flow of our programs which is normally sequential. i.e. statements are executed one after the other in order. Changing this sequential execution in a controlled way is called transfer of control and is achieved using control structures.
The C++ control structures are identical with those of C. The relational operators ( > , < , >= , <= ), equality operators ( == , != ), and the logical operators (&& , || , !) are used in logical tests which produce either true or false (0).
Typically, the following operators are used to form a logical test for the control structures which determines what action should be taken:
- the relational operators: > , < , >= , and <= (which are used to compare two expressions)
- the equality operators: == and != (which are used to check two expressions for equality)
- the logical operators: && , || , and !
The result of the above operators is of type bool, either true (i.e. 1), or false (i.e. 0). You should never compare floating-point values for equality or inequality, since the floating-point numbers can, in general, only be approximated since only a small number of digits are used for computer representation of values.
In all control structures, a simple (i.e. single), or a compound, i.e. a sequence of statements enclosed in curly braces, statement is either conditionally or repeatedly executed, based on a logical test.
Theif and if-else, as well as the switch control structures are used to make certain selections
The simplest selection control structure is the if, where if the logical test is true (i.e. non zero), then the following statement (or statements in curly braces) is (are) executed. Otherwise the statement (or statements) is (are) skipped and the statement after the if control structure is executed.
if (logical test)
statement ;
if-else if-...-else control structure provides several alternative actions. (It is more efficient to put the most probable selection first to reduce the chances of multiple checks)
if (logical test) (if-else if -else provides alternative actions)
{
statements (executed if (logical test) is true)
}
else if (another logical test) (checked if (logical test) is false)
{
statements
}
else (executed if not any (logical test) is true)
{
statements
}
Theswitch() control structure is useful when there are many different selections. It consists of multiple selection cases and an optional default case. Only constant integral expressions can be checked in switch() cases. The controlling expression in the parentheses determines which of the cases should be executed and starts executing statements in that case continuing until the closing brace of the switch control structure, or until a break is reached. The break causes the program to exit the switch structure and execute the next statement after it.
switch (x)
{
case 1:
statements
break;
case 2: case ’b’: case ’B’:
statements
break;
case 3:
case ’c’:
statements
break;
........
default:
.........
}
while, do/while, and for are the repetition control structures of C++, i.e. are used when iterations are required.
The statements of the while control structure are executed repeatedly as long as the logical test is true, and, at each iteration when the closing brace is reached, control is passed back at the beginning of the while loop. The while loop is continuously repeated until the logical test becomes false (i.e. equal to zero)
while(logical test)
{
statements // statements executed repeatedly as
// long as the logical test is true
}
The do/while is similar to while with the only difference that its body is executed at least once since the check is done at the end.
do
{
statements // executed repeatedly as long as the logical test
} while(logical test); // is true but always executed at least once
The for control structure is used for repetitions, when we have a regular incrementing. First, expr1 is evaluated, which is usually used to initialize the loop variables. Then, the logical test (which is a loop continuation condition) is evaluated, and if it is true (i.e. nonzero), the following statements, within the curly braces, are executed. Finally, expr3 is executed (usually providing an increment or decrement of the control variable), and then the procedure from evaluation of the logical test is repeated, as long as it is true (i.e. non zero.) expr1 and expr2 can be comma separated lists of expressions, which are executed from left to right. All three expressions are optional, although the two semicolon are always required.
for (expr1 ; logical test ; expr3)
{
statements in body of for loop
}
Finally, the following control structure is the conditional operator which produces a value based on the logical test. Therefore, it can be placed inside another expression. The first expression after the question mark is executed if the logical test is true. Otherwise, the expression after the colon is executed.
( logical test) ? when_true_statement : when_false_statement ;
e.g.: max = (x>y) ? x : y ;
(i%2) ? cout << i << " is an odd integer" : cout << i << " is an even integer" ;
The break statement is typically used to skip the remainder of the switch statement. It is also used to exit repetition control structures (i.e. while, do/while and for). In all these cases execution continues with the first statement after the terminated control structure. The break statement exits the innermost loop, or switch statement.
The continue statement is used to skip the current iteration of a repetition control structure and continue with the next iteration, if there is one, i.e. the current iteration only is terminated and execution continues with the evaluation of the logical test of the next iteration. It goes to the next iteration of the innermost loop.