0% found this document useful (0 votes)
16 views149 pages

Course Lecture Notes

Lecture note

Uploaded by

Tolesa Shore
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
0% found this document useful (0 votes)
16 views149 pages

Course Lecture Notes

Lecture note

Uploaded by

Tolesa Shore
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
Download as pdf or txt
You are on page 1/ 149

Chapter One

1.1 Introduction to programming


A Computer is an electronic device that accepts data, performs computations, and makes
logical decisions according to instructions that have been given to it; then produces
meaningful information in a form that is useful to the user. In current world we live in,
computers are almost used in all walks of life for different purposes. They have been
deployed to solve different real life problems, from the simplest game playing up to the
complex nuclear energy production. Computers are important and widely used in our
society because they are cost-effective aids to problem solving in business, government,
industry, education, etc.
In order to solve a given problem, computers must be given the correct instruction about
how they can solve it. The terms computer programs, software programs, or just
programs are the instructions that tells the computer what to do. Computer requires
programs to function, and a computer programs does nothing unless its instructions are
executed by a CPU. Computer programming (often shortened to programming or
coding) is the process of writing, testing, debugging/troubleshooting, and maintaining the
source code of computer programs. Writing computer programs means writing instructions
that will make the computer follow and run a program based on those instructions. Each
instruction is relatively simple, yet because of the computer's speed, it is able to run
millions of instructions in a second. A computer program usually consists of two elements:
 Data – characteristics
 Code – action
Computer programs (also know as source code) is often written by professionals known as
Computer Programmers (simply programmers). Source code is written in one of
programming languages. A programming language is an artificial language that can be
used to control the behavior of a machine, particularly a computer. Programming
languages, like natural language (such as English or others), are defined by syntactic and
semantic rules which describe their structure and meaning respectively. The syntax of a
language describes the possible combinations of symbols that form a syntactically correct
program. The meaning given to a combination of symbols is handled by semantics. Many

1
programming languages have some form of written specification of their syntax and
semantics; some are defined only by an official implementation. In general, programming
languages allow humans to communicate instructions to machines.
A main purpose of programming languages is to provide instructions to a computer. As
such, programming languages differ from most other forms of human expression in that
they require a greater degree of precision and completeness. When using a natural language
to communicate with other people, human authors and speakers can be ambiguous and
make small errors, and still expect their intent to be understood. However, computers do
exactly what they are told to do, and cannot understand the code the programmer
"intended" to write. So computers need to be instructed to perform all the tasks. The
combination of the language definition, the program, and the program's inputs must fully
specify the external behavior that occurs when the program is executed. Computer
languages have relatively few, exactly defined, rules for composition of programs, and
strictly controlled vocabularies in which unknown words must be defined before they can
be used.
Available programming languages come in a variety of forms and types. Thousands of
different programming languages have been developed, used, and discarded. Programming
languages can be divided in to two major categories: low-level and high-level languages.
Low-level languages
Computers only understand one language and that is binary language or the language of 1s
and 0s. Binary language is also known as machine language, one of low-level languages.
In the initial years of computer programming, all the instructions were given in binary
form. Although the computer easily understood these programs, it proved too difficult for a
normal human being to remember all the instructions in the form of 0s and 1s. Therefore,
computers remained mystery to a common person until other languages such as assembly
language was developed, which were easier to learn and understand. Assembly language
correspondences symbolic instructions and executable machine codes and was created to
use letters (called mnemonics) to each machine language instructions to make it easier to
remember or write. For example:
ADD A, B – adds two numbers in memory location A and B

2
Assembly language is nothing more than a symbolic representation of machine code, which
allows symbolic designation of memory locations. However, no matter how close assembly
language is to machine code, computers still cannot understand it. The assembly language
must be translated to machine code by a separate program called assembler. The machine
instruction created by the assembler from the original program (source code) is called
object code. Thus assembly languages are unique to a specific computer (machine).
Assemblers are written for each unique machine language.
High-level languages
Although programming in assembly language is not as difficult and error prone as stringing
together ones and zeros, it is slow and cumbersome. In addition it is hardware specific. The
lack of portability between different computers led to the development of high-level
languages—so called because they permitted a programmer to ignore many low-level
details of the computer's hardware. Further, it was recognized that the closer the syntax,
rules, and mnemonics of the programming language could be to "natural language" the less
likely it became that the programmer would inadvertently introduce errors (called "bugs")
into the program. High-level languages are more English-like and, therefore, make it easier
for programmers to "think" in the programming language. High-level languages also
require translation to machine language before execution. This translation is accomplished
by either a compiler or an interpreter. Compilers translate the entire source code program
before execution. Interpreters translate source code programs one line at a time. Interpreters
are more interactive than compilers. FORTRAN (FORmula TRANslator), BASIC (Bingers
All Purpose Symbolic Instruction Code), PASCAL, C, C++, Java are some examples of
high-level languages.
The question of which language is best is one that consumes a lot of time and energy
among computer professionals. Every language has its strengths and weaknesses. For
example, FORTRAN is a particularly good language for processing numerical data, but it
does not lend itself very well to organizing large programs. Pascal is very good for writing
well-structured and readable programs, but it is not as flexible as the C programming
language. C++ embodies powerful object-oriented features
As might be expected in a dynamic and evolving field, there is no single standard for
classifying programming languages. Another most fundamental ways programming

3
languages are characterized (categorized) is by programming paradigm. A programming
paradigm provides the programmer's view of code execution. The most influential
paradigms are examined in the next three sections, in approximate chronological order.
Procedural Programming Languages
Procedural programming specifies a list of operations that the program must complete to
reach the desired state. Each program has a starting state, a list of operations to complete,
and an ending point. This approach is also known as imperative programming. Integral to
the idea of procedural programming is the concept of a procedure call.
Procedures, also known as functions, subroutines, or methods, are small sections of code
that perform a particular function. A procedure is effectively a list of computations to be
carried out. Procedural programming can be compared to unstructured programming,
where all of the code resides in a single large block. By splitting the programmatic tasks
into small pieces, procedural programming allows a section of code to be re-used in the
program without making multiple copies. It also makes it easier for programmers to
understand and maintain program structure.
Two of the most popular procedural programming languages are FORTRAN and BASIC.
Structured Programming Languages
Structured programming is a special type of procedural programming. It provides
additional tools to manage the problems that larger programs were creating. Structured
programming requires that programmers break program structure into small pieces of code
that are easily understood. It also frowns upon the use of global variables and instead uses
variables local to each subroutine. One of the well-known features of structural
programming is that it does not allow the use of the GOTO statement. It is often associated
with a "top-down" approach to design. The top-down approach begins with an initial
overview of the system that contains minimal details about the different parts. Subsequent
design iterations then add increasing detail to the components until the design is complete.
The most popular structured programming languages include C, Ada, and Pascal.
Object-Oriented Programming Languages
Object-oriented programming is one the newest and most powerful paradigms. In object-
oriented programs, the designer specifies both the data structures and the types of
operations that can be applied to those data structures. This pairing of a piece of data with

4
the operations that can be performed on it is known as an object. A program thus becomes
a collection of cooperating objects, rather than a list of instructions. Objects can store state
information and interact with other objects, but generally each object has a distinct, limited
role.
1.2 Problem solving Techniques
Computer solves varieties of problems that can be expressed in a finite number of steps
leading to a precisely defined goal by writing different programs. A program is not needed
only to solve a problem but also it should be reliable, (maintainable) portable and efficient.
In computer programming two facts are given more weight:
 The first part focuses on defining the problem and logical procedures to follow in
solving it.
 The second introduces the means by which programmers communicate those
procedures to the computer system so that it can be executed.
There are system analysis and design tools, particularly flowchart and structure chart, that
can be used to define the problem in terms of the steps to its solution. The programmer uses
programming language to communicate the logic of the solution to the computer.
Before a program is written, the programmer must clearly understand what data are to be
used, the desired result, and the procedure to be used to produce the result. The procedure,
or solution, selected is referred to as an algorithm. An algorithm is defined as a step-by-
step sequence of instructions that must terminate and describe how the data is to be
processed to produce the desired outputs. Simply, algorithm is a sequence of instructions.
Algorithms are a fundamental part of computing. There are three commonly used tools to
help to document program logic (the algorithm). These are flowcharts, structured chart,
and Pseudo code. We will use the three methods here. Generally, flowcharts work well for
small problems but Pseudo code is used for larger problems.
1.2.1 Pseudo code
Pseudocode (derived from pseudo and code) is a compact and informal high-level
description of a computer algorithm that uses the structural conventions of programming
languages, but typically omits detailes such as subroutines, variables declarations and
system-specific syntax. The programming language is augmented with natural language
descriptions of the details, where convenient, or with compact mathematical notation. The

5
purpose of using pseudocode is that it may be easier for humans to read than conventional
programming languages, and that it may be a compact and environment-independent
generic description of the key principles of an algorithm. No standard for pseudocode
syntax exists, as a program in pseudocode is not an executable program. As the name
suggests, pseudocode generally does not actually obey the synatx rules of any particular
language; there is no systematic standard form, although any particular writer will
generally borrow the appearance of a particular language.
The programming process is a complicated one. You must first understand the program
specifications, of course, Then you need to organize your thoughts and create the program.
This is a difficult task when the program is not trivial (i.e. easy). You must break the main
tasks that must be accomplished into smaller ones in order to be able to eventually write
fully developed code. Writing pseudocode will save you time later during the construction
& testing phase of a program's development.
Example:
Original Program Specification:
Write a program that obtains two integer numbers from the user. It will print out the
sum of those numbers.
Pseudocode:
Prompt the user to enter the first integer
Prompt the user to enter a second integer
Compute the sum of the two user inputs
Display an output prompt that explains the answer as the sum
Display the result
1.2.2 Structured Charts
Structured chart depicts the logical functions to the solution of the problem using a chart. It
provides an overview that confirms the solution to the problem without excessive
consideration to detail. It is high-level in nature.
Example: Write a program that asks the user to enter a temperature reading in centigrade
and then prints the equivalent Fahrenheit value.

6
Input Process Output
Centigrade  Prompt for centigrade value Fahrenheit
 Read centigrade value
 Compute Fahrenheit value
 Display Fahrenheit value

CelsusToFarh
(main func)

centigard Fahrenheit
centigard

InPutCen CalcFar OutPutFar

Fahrenheit

1.2.3 Flowchart
A flowchart (also spelled flow-chart and flow chart) is a schematic representation of an
algorithm or a process. The advantage of flowchart is it doesn’t depend on any particular
programming language, so that it can used, to translate an algorithm to more than one
programming language. Flowchart uses different symbols (geometrical shapes) to
represent different processes. The following table shows some of the common symbols.

7
Example 1: - Draw flow chart of an algorithm to add two numbers and display their result.
Algorithm description
 Read the rules of the two numbers (A and B)
 Add A and B
 Assign the sum of A and B to C
 Display the result ( c)
The flow chart is:

Start

Read A, B

C= A+B

Print C

End

8
Example 2: Write an algorithm description and draw a flow chart to check a number is
negative or not.
Algorithm description.
1/ Read a number x
2/ If x is less than zero write a message negative
else write a message not negative

Some times there are conditions in which it is necessary to execute a group of statements
repeatedly. Until some condition is satisfied. This condition is called a loop. Loop is a
sequence of instructions, which is repeated until some specific condition occurs. A loop
normally consists of four parts. These are:
Initialization: - Setting of variables of the computation to their initial values and setting the
counter for determining to exit from the loop.
Computation: - Processing
Test: - Every loop must have some way of exiting from it or else the program would
endlessly remain in a loop.
Increment: - Re-initialization of the loop for the next loop.
Example 3: - Write the algorithmic description and draw a flow chart to find the following
sum.
Sum = 1+2+3+…. + 50
Algorithmic description
1. Initialize sum to 0 and counter to 1
1.1.If the counter is less than or equal to 50
• Add counter to sum

9
• Increase counter by 1
• Repeat step 1.1
1.2.Else
• Exit
2. Write sum

1.3 System Development Life Cycle (SDLC)


The Systems Development Life Cycle (SDLC) is a conceptual model used in project
management that describes the stages involved in a computer system development project
from an initial feasibility study through maintenance of the completed application. The
phases of SDLC are discussed below briefly.
1.3.1 Feasibility study
The first step is to identify a need for the new system. This will include determining
whether a business problem or opportunity exists, conducting a feasibility study to
determine if the proposed solution is cost effective, and developing a project plan.
This process may involve end users who come up with an idea for improving their work or
may only involve IS people. Ideally, the process occurs in tandem with a review of the
organization's strategic plan to ensure that IT is being used to help the organization achieve
its strategic objectives. Management may need to approve concept ideas before any money
is budgeted for its development.
A preliminary analysis, determining the nature and scope of the problems to be solved is
carried out. Possible solutions are proposed, describing the cost and benefits. Finally, a
preliminary plan for decision making is produced.

10
The process of developing a large information system can be very costly, and the
investigation stage may require a preliminary study called a feasibility study, which
includes e.g. the following components:
a. Organizational Feasibility
 How well the proposed system supports the strategic objectives of the organization.
b. Economic Feasibility
 Cost savings
 Increased revenue
 Decreased investment
 Increased profits
c. Technical Feasibility
 Hardware, software, and network capability, reliability, and availability
d. Operational Feasibility
 End user acceptance
 Management support
 Customer, supplier, and government requirements
1.3.2 Requirements analysis
Requirements analysis is the process of analyzing the information needs of the end users,
the organizational environment, and any system presently being used, developing the
functional requirements of a system that can meet the needs of the users. Also, the
requirements should be recorded in a document, email, user interface storyboard,
executable prototype, or some other form. The requirements documentation should be
referred to throughout the rest of the system development process to ensure the developing
project aligns with user needs and requirements.
End users must be involved in this process to ensure that the new system will function
adequately and meets their needs and expectations.
1.3.3 Designing solution
After the requirements have been determined, the necessary specifications for the
hardware, software, people, and data resources, and the information products that will
satisfy the functional requirements of the proposed system can be determined. The design

11
will serve as a blueprint for the system and helps detect problems before these errors or
problems are built into the final system.
The created system design, but must reviewed by users to ensure the design meets users'
needs.
1.3.4 Testing designed solution
A smaller test system is sometimes a good idea in order to get a “proof-of-concept”
validation prior to committing funds for large scale fielding of a system without knowing if
it really works as intended by the user.
1.3.5 Implementation
The real code is written here. Systems implementation is the construction of the new
system and its delivery into production or day-to-day operation. The key to understanding
the implementation phase is to realize that there is a lot more to be done than programming.
Implementation requires programming, but it also requires database creation and
population, and network installation and testing. You also need to make sure the people are
taken care of with effective training and documentation. Finally, if you expect your
development skills to improve over time, you need to conduct a review of the lessons
learned.
1.3.6 Unit testing
Normally programs are written as a series of individual modules, these subject to separate
and detailed test.
1.3.7 Integration and System testing
Brings all the pieces together into a special testing environment, then checks for errors,
bugs and interoperability. The system is tested to ensure that interfaces between modules
work (integration testing), the system works on the intended platform and with the
expected volume of data (volume testing) and that the system does what the user requires
(acceptance/beta testing).
1.3.8 Maintenance
What happens during the rest of the software's life: changes, correction, additions, moves to
a different computing platform and more. This, the least glamorous and perhaps most
important step of all, goes on seemingly forever.

12
Chapter Two

2. C++ Basics ........................................................................................2


2.1. Structure of C++ Program................................................................................... 2
2.2. C++ IDE.............................................................................................................. 2
2.3. Showing Sample program................................................................................... 3
2.4. Basic Elements.................................................................................................... 5
2.4.1. Keywords (reserved words) ........................................................................ 5
2.4.2. Identifiers .................................................................................................... 5
2.4.3. Literals ........................................................................................................ 6
2.4.4. Comments ................................................................................................... 6
2.5. Data Types, Variables, and Constants ................................................................ 7
2.5.1. Variables ..................................................................................................... 7
2.5.2. Basic Data Types ........................................................................................ 8
2.5.3. Signed and Unsigned .................................................................................. 9
2.5.4. Characters ................................................................................................. 12
2.5.5. Characters and Numbers ........................................................................... 12
2.6. Operators........................................................................................................... 13
2.6.1. Assignment Operators............................................................................... 13
2.6.2. Arithmetic Operators ................................................................................ 14
2.6.3. Relational Operators ................................................................................. 16
2.6.4. Logical Operators...................................................................................... 17
2.6.5. Bitwise Operators...................................................................................... 17
2.6.6. Increment/decrement Operators................................................................ 19
2.7. Precedence of Operators ................................................................................... 19
2.8. Simple Type Conversion................................................................................... 21
2.9. Statements ......................................................................................................... 21
2.10. Input/Output Statements ............................................................................... 22

-1-
2. C++ Basics
2.1. Structure of C++ Program
A C++ program has the following structure
[Comments]
[Preprocessor directives]
[Global variable declarations]
[Prototypes of functions]
[Definitions of functions]
2.2. C++ IDE
The complete development cycle in C++ is: Write the program, compile the source code,
link the program, and run it.
Writing a Program
To write a source code, your compiler may have its own built-in text editor, or you may
be using a commercial text editor or word processor that can produce text files. The
important thing is that whatever you write your program in, it must save simple, plain-
text files, with no word processing commands embedded in the text. Examples of safe
editors include Windows Notepad, the DOS Edit command, EMACS, and vi. Many
commercial word processors, such as WordPerfect, Word, and dozens of others, also
offer a method for saving simple text files.
The files you create with your editor are called source files, and for C++ they typically
are named with the extension .CPP.
Compiling
Your source code file can't be executed, or run, as a program can. To turn your source
code into a program, you use a compiler. How you invoke your compiler, and how you
tell it where to find your source code, will vary from compiler to compiler; check your
documentation. In Borland's Turbo C++ you pick the RUN menu command or type
tc <filename>
from the command line, where <filename> is the name of your source code file (for
example, test.cpp). Other compilers may do things slightly differently. After your source
code is compiled, an object file is produced. This file is often named with the extension

-2-
.OBJ. This is still not an executable program, however. To turn this into an executable
program, you must run your linker.
Linking
C++ programs are typically created by linking together one or more OBJ files with one or
more libraries. A library is a collection of linkable files that were supplied with your
compiler, that you purchased separately, or that you created and compiled. All C++
compilers come with a library of useful functions (or procedures) and classes that you can
include in your program. A function is a block of code that performs a service, such as
adding two numbers or printing to the screen. A class is a collection of data and related
functions.
Summary
The steps to create an executable file are
1. Create a source code file, with a .CPP extension.
2. Compile the source code into a file with the .OBJ extension.
3. Link your OBJ file with any needed libraries to produce an executable program.
2.3. Showing Sample program
Any meaningful program written in C++ has to contain a number of components: the
main function; some variable declarations; and some executable statements. For example,
the following is a very basic C++ program:
1: #include <iostream.h>
2:
3: int main()
4: {
5: cout << "Hello World!\n";
6: return 0;
7: }
On line 1, the file iostream.h is included in the file. The first character is the # symbol,
which is a signal to the preprocessor. Each time you start your compiler, the preprocessor
is run. The preprocessor reads through your source code, looking for lines that begin with
the pound symbol (#), and acts on those lines before the compiler runs.
include is a preprocessor instruction that says, "What follows is a filename. Find that file
and read it in right here." The angle brackets around the filename tell the preprocessor to

-3-
look in all the usual places for this file. If your compiler is set up correctly, the angle
brackets will cause the preprocessor to look for the file iostream.h in the directory that
holds all the H files for your compiler. The file iostream.h (Input-Output-Stream) is used
by cout, which assists with writing to the screen. The effect of line 1 is to include the file
iostream.h into this program as if you had typed it in yourself.
The preprocessor runs before your compiler each time the compiler is invoked. The
preprocessor translates any line that begins with a pound symbol (#) into a special
command, getting your code file ready for the compiler.
Line 3 begins the actual program with a function named main(). Every C++ program has
a main() function. In general, a function is a block of code that performs one or more
actions. Usually functions are invoked or called by other functions, but main() is special.
When your program starts, main() is called automatically.
main(), like all functions, must state what kind of value it will return. The return value
type for main() in HELLO.CPP is int, which means that this function will return an
integer value.
All functions begin with an opening brace ({) and end with a closing brace (}). The
braces for the main() function are on lines 4 and 7. Everything between the opening and
closing braces is considered a part of the function.
The meat and potatoes of this program is on line 5. The object cout is used to print a
message to the screen. cout is used in C++ to print strings and values to the screen. A
string is just a set of characters.
Here's how cout is used: type the word cout, followed by the output redirection operator
(<<). Whatever follows the output redirection operator is written to the screen. If you
want a string of characters written, be sure to enclose them in double quotes ("), as shown
on line 5. A text string is a series of printable characters.
The final two characters, \n, tell cout to put a new line after the words Hello World! All
ANSI-compliant programs declare main() to return an int. This value is "returned" to the
operating system when your program completes. Some programmers signal an error by
returning the value 1.
The main() function ends on line 7 with the closing brace.

-4-
2.4. Basic Elements
2.4.1. Keywords (reserved words)
Reserved/Key words have a unique meaning within a C++ program. These symbols, the
reserved words, must not be used for any other purposes. All reserved words are in
lower-case letters. The following are some of the reserved words of C++.
asm auto bool break case catch
const_cast class const char continue default
dynamic_cast do double delete else enum
explicit extern false float for friend
goto if inline int long mutable
namespace new operator private protected public
reinterpret_cast register return short signed sizeof
static_cast static struct switch template this
throw true try typedef typeid typename
union unsigned using virtual void volatile
wchar_t
Notice that main is not a reserved word. However, this is a fairly technical distinction,
and for practical purposes you are advised to treat main, cin, and cout as if they were
reserved as well.
2.4.2. Identifiers
An identifier is name associated with a function or data object and used to refer to that
function or data object. An identifier must:
 Start with a letter or underscore
 Consist only of letters, the digits 0-9, or the underscore symbol _
 Not be a reserved word
Syntax of an identifier
Letter

Letter
-
Digit

-5-
For the purposes of C++ identifiers, the underscore symbol, _, is considered to be a letter.
Its use as the first character in an identifier is not recommended though, because many
library functions in C++ use such identifiers. Similarly, the use of two consecutive
underscore symbols, _ _, is forbidden.
The following are valid identifiers
Length days_in_year DataSet1 Profit95
Int _Pressure first_one first_1
Although using _Pressure is not recommended.
The following are invalid:
days-in-year 1data int first.val
throw my__best No## bestWish!
Although it may be easier to type a program consisting of single character identifiers,
modifying or correcting the program becomes more and more difficult. The minor typing
effort of using meaningful identifiers will repay itself many fold in the avoidance of
simple programming errors when the program is modified.
At this stage it is worth noting that C++ is case-sensitive. That is lower-case letters are
treated as distinct from upper-case letters. Thus the word NUM different from the word
num or the word Num. Identifiers can be used to identify variable or constants or
functions. Function identifier is an identifier that is used to name a function.
2.4.3. Literals
Literals are constant values which can be a number, a character of a string. For example
the number 129.005, the character ‘A’ and the string “hello world” are all literals. There
is no identifier that identifies them.
2.4.4. Comments
A comment is a piece of descriptive text which explains some aspect of a program.
Program comments are totally ignored by the compiler and are only intended for human
readers. C++ provides two types of comment delimiters:
 Anything after // (until the end of the line on which it appears) is considered a
comment.
 Anything enclosed by the pair /* and */ is considered a comment.

-6-
2.5. Data Types, Variables, and Constants
2.5.1. Variables
A variable is a symbolic name for a memory location in which data can be stored and
subsequently recalled. Variables are used for holding data values so that they can be
utilized in various computations in a program. All variables have two important
attributes:
 A type, which is, established when the variable is defined (e.g., integer, float,
character). Once defined, the type of a C++ variable cannot be changed.
 A value, which can be changed by assigning a new value to the variable. The kind
of values a variable can assume depends on its type. For example, an integer
variable can only take integer values (e.g., 2, 100, -12) not real numbers like
0.123.
Variable Declaration
Declaring a variable means defining (creating) a variable. You create or define a variable
by stating its type, followed by one or more spaces, followed by the variable name and a
semicolon. The variable name can be virtually any combination of letters, but cannot
contain spaces and the first character must be a letter or an underscore. Variable names
cannot also be the same as keywords used by C++. Legal variable names include x,
J23qrsnf, and myAge. Good variable names tell you what the variables are for; using
good names makes it easier to understand the flow of your program. The following
statement defines an integer variable called myAge:
int myAge;
IMPORTANT- Variables must be declared before used!
As a general programming practice, avoid such horrific names as J23qrsnf, and restrict
single-letter variable names (such as x or i) to variables that are used only very briefly.
Try to use expressive names such as myAge or howMany.
A point worth mentioning again here is that C++ is case-sensitive. In other words,
uppercase and lowercase letters are considered to be different. A variable named age is
different from Age, which is different from AGE.

Creating More Than One Variable at a Time

-7-
You can create more than one variable of the same type in one statement by writing the
type and then the variable names, separated by commas. For example:
int myAge, myWeight; // two int variables
long area, width, length; // three longs
As you can see, myAge and myWeight are each declared as integer variables. The second
line declares three individual long variables named area, width, and length. However
keep in mind that you cannot mix types in one definition statement.
Assigning Values to Your Variables
You assign a value to a variable by using the assignment operator (=). Thus, you would
assign 5 to Width by writing
int Width;
Width = 5;
You can combine these steps and initialize Width when you define it by writing
int Width = 5;
Initialization looks very much like assignment, and with integer variables, the difference
is minor. The essential difference is that initialization takes place at the moment you
create the variable.
Just as you can define more than one variable at a time, you can initialize more than one
variable at creation. For example:
// create two int variables and initialize them
int width = 5, length = 7;
This example initializes the integer variable width to the value 5 and the length variable
to the value 7. It is possible to even mix definitions and initializations:
int myAge = 39, yourAge, hisAge = 40;
This example creates three type int variables, and it initializes the first and third.
2.5.2. Basic Data Types
When you define a variable in C++, you must tell the compiler what kind of variable it is:
an integer, a character, and so forth. This information tells the compiler how much room
to set aside and what kind of value you want to store in your variable.
Several data types are built into C++. The varieties of data types allow programmers to
select the type appropriate to the needs of the applications being developed. The data

-8-
types supported by C++ can be classified as basic (fundamental) data types, user defined
data types, derived data types and empty data types. However, the discussion here will
focus only on the basic data types.
Basic (fundamental) data types in C++ can be conveniently divided into numeric and
character types. Numeric variables can further be divided into integer variables and
floating-point variables. Integer variables will hold only integers whereas floating
number variables can accommodate real numbers.
Both the numeric data types offer modifiers that are used to vary the nature of the data to
be stored. The modifiers used can be short, long, signed and unsigned.
The data types used in C++ programs are described in Table 1.1. This table shows the
variable type, how much room it takes in memory, and what kinds of values can be stored
in these variables. The values that can be stored are determined by the size of the variable
types.

Type Size Values


unsigned short int 2 bytes 0 to 65,535
short int(signed short int) 2 bytes -32,768 to 32,767
unsigned long int 4 bytes 0 to 4,294,967,295
long int(signed long int) 4 bytes -2,147,483,648 to 2,147,483,647
int 2 bytes -32,768 to 32,767
unsigned int 2 bytes 0 to 65,535
signed int 2 bytes -32,768 to 32,767
char 1 byte 256 character values
float 4 bytes 3.4e-38 to 3.4e38
double 8 bytes 1.7e-308 to 1.7e308
long double 10 bytes 1.2e-4932 to 1.2e4932
Table C++ data types and their ranges
2.5.3. Signed and Unsigned
As shown above, integer types come in two varieties: signed and unsigned. The idea here
is that sometimes you need negative numbers, and sometimes you don't. Integers (short

-9-
and long) without the word "unsigned" are assumed to be signed. Signed integers are
either negative or positive. Unsigned integers are always positive.
Because you have the same number of bytes for both signed and unsigned integers, the
largest number you can store in an unsigned integer is twice as big as the largest positive
number you can store in a signed integer. An unsigned short integer can handle numbers
from 0 to 65,535. Half the numbers represented by a signed short are negative, thus a
signed short can only represent numbers from -32,768 to 32,767.
Example: A demonstration of the use of variables.
2: #include <iostream.h>
3:
4: int main()
5: {
6: unsigned short int Width = 5, Length;
7: Length = 10;
8:
9: // create an unsigned short and initialize with result
10: // of multiplying Width by Length
11: unsigned short int Area = Width * Length;
12:
13: cout << "Width:" << Width << "\n";
14: cout << "Length: " << Length << endl;
15: cout << "Area: " << Area << endl;
16: return 0;
17: }
Output: Width:5
Length: 10
Area: 50
Line 2 includes the required include statement for the iostream's library so that cout will
work. Line 4 begins the program.
On line 6, Width is defined as an unsigned short integer, and its value is initialized to 5.
Another unsigned short integer, Length, is also defined, but it is not initialized. On line 7,
the value 10 is assigned to Length.

- 10 -
On line 11, an unsigned short integer, Area, is defined, and it is initialized with the value
obtained by multiplying Width times Length. On lines 13-15, the values of the variables
are printed to the screen. Note that the special word endl creates a new line.
Wrapping around integer values
The fact that unsigned long integers have a limit to the values they can hold is only rarely
a problem, but what happens if you do run out of room? When an unsigned integer
reaches its maximum value, it wraps around and starts over, much as a car odometer
might. The following example shows what happens if you try to put too large a value into
a short integer.
Example: A demonstration of putting too large a value in a variable
1: #include <iostream.h>
2: int main()
3: {
4: unsigned short int smallNumber;
5: smallNumber = 65535;
6: cout << "small number:" << smallNumber << endl;
7: smallNumber++;
8: cout << "small number:" << smallNumber << endl;
9: smallNumber++;
10: cout << "small number:" << smallNumber << endl;
11: return 0;
12: }
Output: small number:65535
small number:0
small number:1
A signed integer is different from an unsigned integer, in that half of the values you can
represent are negative. Instead of picturing a traditional car odometer, you might picture
one that rotates up for positive numbers and down for negative numbers. One mile from 0
is either 1 or -1. When you run out of positive numbers, you run right into the largest
negative numbers and then count back down to 0. The whole idea here is putting a
number that is above the range of the variable can create unpredictable problem.

- 11 -
Example: A demonstration of adding too large a number to a signed integer.
1: #include <iostream.h>
2: int main()
3: {
4: short int smallNumber;
5: smallNumber = 32767;
6: cout << "small number:" << smallNumber << endl;
7: smallNumber++;
8: cout << "small number:" << smallNumber << endl;
9: smallNumber++;
10: cout << "small number:" << smallNumber << endl;
11: return 0;
12: }
Output: small number:32767
small number:-32768
small number:-32767
IMPORTANT – To any variable, do not assign a value that is beyond its range!
2.5.4. Characters
Character variables (type char) are typically 1 byte, enough to hold 256 values. A char
can be interpreted as a small number (0-255) or as a member of the ASCII set. ASCII
stands for the American Standard Code for Information Interchange. The ASCII character
set and its ISO (International Standards Organization) equivalent are a way to encode all
the letters, numerals, and punctuation marks.
In the ASCII code, the lowercase letter "a" is assigned the value 97. All the lower- and
uppercase letters, all the numerals, and all the punctuation marks are assigned values
between 1 and 128. Another 128 marks and symbols are reserved for use by the computer
maker, although the IBM extended character set has become something of a standard.
2.5.5. Characters and Numbers
When you put a character, for example, `a', into a char variable, what is really there is just
a number between 0 and 255. The compiler knows, however, how to translate back and
forth between characters (represented by a single quotation mark and then a letter,
numeral, or punctuation mark, followed by a closing single quotation mark) and one of
the ASCII values.

- 12 -
The value/letter relationship is arbitrary; there is no particular reason that the lowercase
"a" is assigned the value 97. As long as everyone (your keyboard, compiler, and screen)
agrees, there is no problem. It is important to realize, however, that there is a big
difference between the value 5 and the character `5'. The latter is actually valued at 53,
much as the letter `a' is valued at 97.
2.6. Operators
C++ provides operators for composing arithmetic, relational, logical, bitwise, and
conditional expressions. It also provides operators which produce useful side-effects,
such as assignment, increment, and decrement. We will look at each category of
operators in turn. We will also discuss the precedence rules which govern the order of
operator evaluation in a multi-operator expression.
2.6.1. Assignment Operators
The assignment operator is used for storing a value at some memory location (typically
denoted by a variable). Its left operand should be an lvalue, and its right operand may be
an arbitrary expression. The latter is evaluated and the outcome is stored in the location
denoted by the lvalue.
An lvalue (standing for left value) is anything that denotes a memory location in which a
value may be stored. The only kind of lvalue we have seen so far is a variable. Other
kinds of lvalues (based on pointers and references) will be described later. The
assignment operator has a number of variants, obtained by combining it with the
arithmetic and bitwise operators.
Operato Example Equivalent To
r
= n = 25
+= n += 25 n = n + 25
-= n -= 25 n = n - 25
*= n *= 25 n = n * 25
/= n /= 25 n = n / 25
%= n %= 25 n = n % 25
&= n &= 0xF2F2 n = n & 0xF2F2

- 13 -
|= n |= 0xF2F2 n = n | 0xF2F2
^= n ^= 0xF2F2 n = n ^ 0xF2F2
<<= n <<= 4 n = n << 4
>>= n >>= 4 n = n >> 4

An assignment operation is itself an expression whose value is the value stored in its left
operand. An assignment operation can therefore be used as the right operand of another
assignment operation. Any number of assignments can be concatenated in this fashion to
form one expression. For example:
int m, n, p;
m = n = p = 100; // means: n = (m = (p = 100));
m = (n = p = 100) + 2; // means: m = (n = (p = 100)) + 2;
This is equally applicable to other forms of assignment. For example:
m = 100;
m += n = p = 10; // means: m = m + (n = p = 10);
2.6.2. Arithmetic Operators
C++ provides five basic arithmetic operators. These are summarized in table below

Operator Name Example


+ Addition 12 + 4.9 // gives 16.9
- Subtraction 3.98 - 4 // gives -0.02
* Multiplication 2 * 3.4 // gives 6.8
/ Division 9 / 2.0 // gives 4.5
% Remainder 13 % 3 //gives 1
Arithmetic operators.

Except for remainder (%) all other arithmetic operators can accept a mix of integer and
real operands. Generally, if both operands are integers then the result will be an integer.
However, if one or both of the operands are reals then the result will be a real (or double
to be exact).

- 14 -
When both operands of the division operator (/) are integers then the division is
performed as an integer division and not the normal division we are used to. Integer
division always results in an integer outcome (i.e., the result is always rounded down).
For example:
9/2 // gives 4, not 4.5!
-9 / 2 // gives -5, not -4!
Unintended integer divisions are a common source of programming errors. To obtain a
real division when both operands are integers, you should cast one of the operands to be
real:
int cost = 100;
int volume = 80;
double unitPrice = cost / (double) volume; // gives 1.25

The remainder operator (%) expects integers for both of its operands. It returns the
remainder of integer-dividing the operands. For example 13%3 is calculated by integer
dividing 13 by 3 to give an outcome of 4 and a remainder of 1; the result is therefore 1.
It is possible for the outcome of an arithmetic operation to be too large for storing in a
designated variable. This situation is called an overflow. The outcome of an overflow is
machine-dependent and therefore undefined. For example:
unsigned char k = 10 * 92; // overflow: 920 > 255
It is illegal to divide a number by zero. This results in a run-time division-by-zero failure,
which typically causes the program to terminate.
There are also a number of predefined library functions, which perform arithmetic
operations. As with input & output statements, if you want to use these you must put a
#include statement at the start of your program. Some of the more common library
functions are summarised below.

Header Parameter Result


Function Type(s) Result
File Type
<stdlib.h> abs(i) int int Absolute value of i
<math.h> cos(x) float float Cosine of x (x is in radians)
<math.h> fabs(x) float float Absolute value of x

- 15 -
<math.h> pow(x, y) float float x raised to the power of y
<math.h> sin(x) float float Sine of x (x is in radians)
<math.h> sqrt(x) float float Square root of x
<math.h> tan(x) float float Tangent of x

2.6.3. Relational Operators


C++ provides six relational operators for comparing numeric quantities. These are
summarized in table below. Relational operators evaluate to 1 (representing the true
outcome) or 0 (representing the false outcome).
Operator Name Example
== Equality 5 == 5 // gives 1
!= Inequality 5 != 5 // gives 0
< Less Than 5 < 5.5 // gives 1
<= Less Than or Equal 5 <= 5 // gives 1
> Greater Than 5 > 5.5 // gives 0
>= Greater Than or Equal 6.3 >= 5 // gives 1
Relational operators
Note that the <= and >= operators are only supported in the form shown. In particular, =<
and => are both invalid and do not mean anything.
The operands of a relational operator must evaluate to a number. Characters are valid
operands since they are represented by numeric values. For example (assuming ASCII
coding):
'A' < 'F' // gives 1 (is like 65 < 70)
The relational operators should not be used for comparing strings, because this will result
in the string addresses being compared, not the string contents. For example, the
expression "HELLO" < "BYE" causes the address of "HELLO" to be compared to the
address of "BYE". As these addresses are determined by the compiler (in a machine-
dependent manner), the outcome may be 0 or 1, and is therefore undefined. C++ provides
library functions (e.g., strcmp) for the lexicographic comparison of string.

- 16 -
2.6.4. Logical Operators
C++ provides three logical operators for combining logical expression. These are
summarized in the table below. Like the relational operators, logical operators evaluate to
1 or 0.
Operator Name Example
! Logical Negation !(5 == 5) // gives 0
&& Logical And 5 < 6 && 6 < 6 // gives 1
|| Logical Or 5 < 6 || 6 < 5 // gives 1
Logical operators

Logical negation is a unary operator, which negates the logical value of its single
operand. If its operand is nonzero it produces 0, and if it is 0 it produces 1.
Logical and produces 0 if one or both of its operands evaluate to 0. Otherwise, it
produces 1. Logical or produces 0 if both of its operands evaluate to 0. Otherwise, it
produces 1.
Note that here we talk of zero and nonzero operands (not zero and 1). In general, any
nonzero value can be used to represent the logical true, whereas only zero represents the
logical false. The following are, therefore, all valid logical expressions:
!20 // gives 0
10 && 5 // gives 1
10 || 5.5 // gives 1
10 && 0 // gives 0
C++ does not have a built-in boolean type. It is customary to use the type int for this
purpose instead. For example:
int sorted = 0; // false
int balanced = 1; // true
2.6.5. Bitwise Operators
C++ provides six bitwise operators for manipulating the individual bits in an integer
quantity. These are summarized in the table below.

- 17 -
Operator Name Example
~ Bitwise Negation ~'\011' // gives '\366'
& Bitwise And '\011' & '\027' // gives '\001'
| Bitwise Or '\011' | '\027' // gives '\037'
^ Bitwise Exclusive '\011' ^ '\027' // gives '\036'
Or
<< Bitwise Left Shift '\011' << 2 // gives '\044'
>> Bitwise Right Shift '\011' >> 2 // gives '\002'
Bitwise operators
Bitwise operators expect their operands to be integer quantities and treat them as bit
sequences. Bitwise negation is a unary operator which reverses the bits in its operands.
Bitwise and compares the corresponding bits of its operands and produces a 1 when both
bits are 1, and 0 otherwise. Bitwise or compares the corresponding bits of its operands
and produces a 0 when both bits are 0, and 1 otherwise. Bitwise exclusive or compares
the corresponding bits of its operands and produces a 0 when both bits are 1 or both bits
are 0, and 1 otherwise.
Bitwise left shift operator and bitwise right shift operator both take a bit sequence as their
left operand and a positive integer quantity n as their right operand. The former produces
a bit sequence equal to the left operand but which has been shifted n bit positions to the
left. The latter produces a bit sequence equal to the left operand but which has been
shifted n bit positions to the right. Vacated bits at either end are set to 0.
Table 2.1 illustrates bit sequences for the sample operands and results in Table 2.Error!
Bookmark not defined.. To avoid worrying about the sign bit (which is machine
dependent), it is common to declare a bit sequence as an unsigned quantity:
unsigned char x = '\011';
unsigned char y = '\027';
Table 2.1 How the bits are calculated.
Example Octal Value Bit Sequence
x 011 0 0 0 0 1 0 0 1
y 027 0 0 0 1 0 1 1 1
~x 366 1 1 1 1 0 1 1 0

- 18 -
x&y 001 0 0 0 0 0 0 0 1
x|y 037 0 0 0 1 1 1 1 1
x^y 036 0 0 0 1 1 1 1 0
x << 2 044 0 0 1 0 0 1 0 0
x >> 2 002 0 0 0 0 0 0 1 0
2.6.6. Increment/decrement Operators
The auto increment (++) and auto decrement (--) operators provide a convenient way of,
respectively, adding and subtracting 1 from a numeric variable. These are summarized in
the following table. The examples assume the following variable definition:
int k = 5;

Operator Name Example


++ Auto Increment (prefix) ++k + 10 // gives 16
++ Auto Increment (postfix) k++ + 10 // gives 15
-- Auto Decrement (prefix) --k + 10 // gives 14
-- Auto Decrement (postfix) k-- + 10 // gives 15
Increment and decrement operators
Both operators can be used in prefix and postfix form. The difference is significant.
When used in prefix form, the operator is first applied and the outcome is then used in the
expression. When used in the postfix form, the expression is evaluated first and then the
operator applied. Both operators may be applied to integer as well as real variables,
although in practice real variables are rarely useful in this form.
2.7. Precedence of Operators
The order in which operators are evaluated in an expression is significant and is
determined by precedence rules. These rules divide the C++ operators into a number of
precedence levels. Operators in higher levels take precedence over operators in lower
levels.

- 19 -
Level Operator Kind Order
Highest :: Unary Both
() [] -> . Binary Left to Right
+ ++ ! * new sizeof() Unary Right to Left
- -- ~ & delete
->* .* Binary Left to Right
* / % Binary Left to Right
+ - Binary Left to Right
<< >> Binary Left to Right
< <= > >= Binary Left to Right
== != Binary Left to Right
& Binary Left to Right
^ Binary Left to Right
| Binary Left to Right
& Binary Left to Right
&
|| Binary Left to Right
?: Ternary Left to Right
= += *= ^= &= <<= Binary Right to Left
-= /= %= |= >>=
Lowest , Binary Left to Right
For example, in
a == b + c * d
c * d is evaluated first because * has a higher precedence than + and ==. The result is
then added to b because + has a higher precedence than ==, and then == is evaluated.
Precedence rules can be overridden using brackets. For example, rewriting the above
expression as
a == (b + c) * d
causes + to be evaluated before *.
Operators with the same precedence level are evaluated in the order specified by the last
column of Table 2.7. For example, in
a = b += c

- 20 -
the evaluation order is right to left, so first b += c is evaluated, followed by a = b.
2.8. Simple Type Conversion
A value in any of the built-in types we have see so far can be converted (type-cast) to any
of the other types. For example:
(int) 3.14 // converts 3.14 to an int to give 3
(long) 3.14 // converts 3.14 to a long to give 3L
(double) 2 // converts 2 to a double to give 2.0
(char) 122 // converts 122 to a char whose code is 122
(unsigned short) 3.14 // gives 3 as an unsigned short
As shown by these examples, the built-in type identifiers can be used as type operators.
Type operators are unary (i.e., take one operand) and appear inside brackets to the left of
their operand. This is called explicit type conversion. When the type name is just one
word, an alternate notation may be used in which the brackets appear around the operand:
int(3.14) // same as: (int) 3.14
In some cases, C++ also performs implicit type conversion. This happens when values of
different types are mixed in an expression. For example:
double d = 1; // d receives 1.0
int i = 10.5; // i receives 10
i = i + d; // means: i = int(double(i) + d)
In the last example, i + d involves mismatching types, so i is first converted to double
(promoted) and then added to d. The result is a double which does not match the type of i
on the left side of the assignment, so it is converted to int (demoted) before being
assigned to i.
The above rules represent some simple but common cases for type conversion.
2.9. Statements
This chapter introduces the various forms of C++ statements for composing programs.
Statements represent the lowest-level building blocks of a program. Roughly speaking,
each statement represents a computational step which has a certain side-effect. (A side-
effect can be thought of as a change in the program state, such as the value of a variable
changing because of an assignment.) Statements are useful because of the side-effects
they cause, the combination of which enables the program to serve a specific purpose
(e.g., sort a list of names).

- 21 -
A running program spends all of its time executing statements. The order in which
statements are executed is called flow control (or control flow). This term reflect the fact
that the currently executing statement has the control of the CPU, which when completed
will be handed over (flow) to another statement. Flow control in a program is typically
sequential, from one statement to the next, but may be diverted to other paths by branch
statements. Flow control is an important consideration because it determines what is
executed during a run and what is not, therefore affecting the overall outcome of the
program.
Like many other procedural languages, C++ provides different forms of statements for
different purposes. Declaration statements are used for defining variables. Assignment-
like statements are used for simple, algebraic computations. Branching statements are
used for specifying alternate paths of execution, depending on the outcome of a logical
condition. Loop statements are used for specifying computations which need to be
repeated until a certain logical condition is satisfied. Flow control statements are used to
divert the execution path to another part of the program. We will discuss these in turn.
2.9.1. Input/Output Statements
The most common way in which a program communicates with the outside world is
through simple, character-oriented Input/Output (IO) operations. C++ provides two
useful operators for this purpose: >> for input and << for output. We have already seen
examples of output using <<. Example 2.1 also illustrates the use of >> for input.
Example
#include <iostream.h>
int main (void)
{
int workDays = 5;
float workHours = 7.5;
float payRate, weeklyPay;
cout << "What is the hourly pay rate? ";
cin >> payRate;
weeklyPay = workDays * workHours * payRate;
cout << "Weekly Pay = ";
cout << weeklyPay;
cout << '\n';
}

- 22 -
Analysis
This line outputs the prompt ‘What is the hourly pay rate? ’ to seek user input.
This line reads the input value typed by the user and copies it to payRate. The input
operator >> takes an input stream as its left operand (cin is the standard C++ input stream
which corresponds to data entered via the keyboard) and a variable (to which the input
data is copied) as its right operand.
When run, the program will produce the following output (user input appears in bold):
What is the hourly pay rate?33.55
Weekly Pay = 1258.125
Both << and >> return their left operand as their result, enabling multiple input or multiple
output operations to be combined into one statement. This is illustrated by example below
which now allows the input of both the daily work hours and the hourly pay rate.
Example
#include <iostream.h>
int main (void)
{
int workDays = 5;
float workHours, payRate, weeklyPay;
cout << "What are the work hours and the hourly pay rate? ";
cin >> workHours >> payRate;
weeklyPay = workDays * workHours * payRate;
cout << "Weekly Pay = " << weeklyPay << '\n';
}
Analysis
This line reads two input values typed by the user and copies them to workHours and
payRate, respectively. The two values should be separated by white space (i.e., one or
more space or tab characters). This statement is equivalent to:
(cin >> workHours) >> payRate;
Because the result of >> is its left operand, (cin >> workHours) evaluates to cin which is
then used as the left operand of the next >> operator.
This line is the result of combining lines 10-12 from example 2.1. It outputs "Weekly Pay
= ", followed by the value of weeklyPay, followed by a newline character. This statement
is equivalent to:
((cout << "Weekly Pay = ") << weeklyPay) << '\n';

- 23 -
Because the result of << is its left operand, (cout << "Weekly Pay = ") evaluates to cout
which is then used as the left operand of the next << operator, etc.
When run, the program will produce the following output:
What are the work hours and the hourly pay rate?7.5 33.55
Weekly Pay = 1258.125

2.9.2. Null statement


Syntax:
;
Description: Do nothing
2.9.3. The block statement
Syntax:
{
[<Declarations>].
<List of statements/statement block>.
}
Any place you can put a single statement, you can put a compound statement, also called
a block. A block begins with an opening brace ({) and ends with a closing brace (}).
Although every statement in the block must end with a semicolon, the block itself does
not end with a semicolon. For example
{
temp = a;
a = b;
b = temp;
}
This block of code acts as one statement and swaps the values in the variables a and b.
2.9.4. The Assignment statement.
Syntax:
<Variable Identifier> = < expression>;
Description:
The <expression> is evaluated and the resulting value is stored in the memory space
reserved for <variable identifier>.
Eg: - int x,y ;
x=5;
y=x+3;
x=y*y;

- 24 -
Chapter Three

3. Control Statements ........................................................................ 1

3.1. INTRODUCTION ............................................................................................................... 1

3.2. CONDITIONAL STATEMENTS .......................................................................................... 1

3.2.1. The if Statement .................................................................................................... 1

3.2.2. The switch Statement ............................................................................................ 3

3.3. LOOPING STATEMENTS .................................................................................................. 5

3.3.1. The ‘for’ Statement ............................................................................................... 6

3.3.2. The ‘while’ Statement ........................................................................................... 5

3.3.3. The ‘do…while’ Statement ................................................................................... 7

3.4. OTHER STATEMENTS ...................................................................................................... 8

3.4.1. The ‘continue’ Statement ...................................................................................... 8

3.4.2. The ‘break’ Statement ........................................................................................... 9

3.4.3. The ‘goto’ Statement ............................................................................................. 9

3.4.4. The ‘return’ Statement......................................................................................... 10


3. Control Statements
3.1.Introduction
A running program spends all of its time executing statements. The order in which statements are
executed is called flow control (or control flow). This term reflect the fact that the currently
executing statement has the control of the CPU, which when completed will be handed over
(flow) to another statement. Flow control in a program is typically sequential, from one statement
to the next, but may be diverted to other paths by branch statements. Flow control is an important
consideration because it determines what is executed during a run and what is not, therefore
affecting the overall outcome of the program.
Like many other procedural languages, C++ provides different forms of statements for different
purposes. Declaration statements are used for defining variables. Assignment-like statements are
used for simple, algebraic computations. Branching statements are used for specifying alternate
paths of execution, depending on the outcome of a logical condition. Loop statements are used for
specifying computations, which need to be repeated until a certain logical condition is satisfied.
Flow control statements are used to divert the execution path to another part of the program. We
will discuss these in turn.

3.2.Conditional Statements
3.2.1. The if Statement
It is sometimes desirable to make the execution of a statement dependent upon a condition being
satisfied. The if statement provides a way of expressing this, the general form of which is:
if (expression)
statement;
First expression is evaluated. If the outcome is nonzero (true) then statement is executed.
Otherwise, nothing happens.
For example, when dividing two values, we may want to check that the denominator is nonzero:

if (count != 0)
average = sum / count;
To make multiple statements dependent on the same condition, we can use a compound statement:
if (balance > 0) {
interest = balance * creditRate;
balance += interest;
}

-1-
A variant form of the if statement allows us to specify two alternative statements: one which is
executed if a condition is satisfied and one which is executed if the condition is not satisfied. This
is called the if-else statement and has the general form:
if (expression)
statement1;
else
statement2;
First expression is evaluated. If the outcome is nonzero (true) then statement1 is executed.
Otherwise, statement2 is executed.
For example:
if (balance > 0) {
interest = balance * creditRate;
balance += interest;
} else {
interest = balance * debitRate;
balance += interest;
}
Given the similarity between the two alternative parts, the whole statement can be simplified to:

if (balance > 0)
interest = balance * creditRate;
else
interest = balance * debitRate;
balance += interest;

Or simplified even further using a conditional expression:


interest = balance * (balance > 0 ? creditRate : debitRate);
balance += interest;

Or just:
balance += balance * (balance > 0 ? creditRate : debitRate);

If statements may be nested by having an if statement appear inside another if statement. For
example:
if (callHour > 6) {
if (callDuration <= 5)
charge = callDuration * tarrif1;
else
charge = 5 * tarrif1 + (callDuration - 5) * tarrif2;
} else
charge = flatFee;
A frequently-used form of nested if statements involves the else part consisting of another if-else
statement. For example:
if (ch >= '0' && ch <= '9')
kind = digit;
else {

-2-
if (ch >= 'A' && ch <= 'Z')
kind = upperLetter;
else {
if (ch >= 'a' && ch <= 'z')
kind = lowerLetter;
else
kind = special;
}
}
For improved readability, it is conventional to format such cases as follows:
if (ch >= '0' && ch <= '9')
kind = digit;
else if (cha >= 'A' && ch <= 'Z')
kind = capitalLetter;
else if (ch >= 'a' && ch <= 'z')
kind = smallLetter;
else
kind = special;

3.2.2. The switch Statement


The switch statement provides a way of choosing between a set of alternatives, based on the value
of an expression. The general form of the switch statement is:
switch (expression) {
case constant1:
statements;
...
case constantn:
statements;
default:
statements;
}
First expression (called the switch tag) is evaluated, and the outcome is compared to each of the
numeric constants (called case labels), in the order they appear, until a match is found. The
statements following the matching case are then executed. Note the plural: each case may be
followed by zero or more statements (not just one statement). Execution continues until either a
break statement is encountered or all intervening statements until the end of the switch statement
are executed. The final default case is optional and is exercised if none of the earlier cases provide a
match.
For example, suppose we have parsed a binary arithmetic operation into its three components and
stored these in variables operator, operand1, and operand2. The following switch statement performs
the operation and stores the result in result.

-3-
switch (operator) {
case '+': result = operand1 + operand2;
break;
case '-': result = operand1 - operand2;
break;
case '*': result = operand1 * operand2;
break;
case '/': result = operand1 / operand2;
break;
default:cout << "unknown operator: " << ch << '\n';
break;
}

As illustrated by this example, it is usually necessary to include a break statement at the end of
each case. The break terminates the switch statement by jumping to the very end of it. There are,
however, situations in which it makes sense to have a case without a break. For example, if we
extend the above statement to also allow x to be used as a multiplication operator, we will have:

switch (operator) {
case '+': result = operand1 + operand2;
break;
case '-': result = operand1 - operand2;
break;
case 'x':
case '*': result = operand1 * operand2;
break;
case '/': result = operand1 / operand2;
break;
default:cout << "unknown operator: " << ch << '\n';
break;
}

Because case 'x' has no break statement (in fact no statement at all!), when this case is satisfied,
execution proceeds to the statements of the next case and the multiplication is performed.
It should be obvious that any switch statement can also be written as multiple if-else statements.
The above statement, for example, may be written as:
if (operator == '+')
result = operand1 + operand2;
else if (operator == '-')
result = operand1 - operand2;
else if (operator == 'x' || operator == '*')
result = operand1 * operand2;
else if (operator == '/')
result = operand1 / operand2;
else
cout << "unknown operator: " << ch << '\n';

However, the switch version is arguably neater in this case. In general, preference should be given
to the switch version when possible. The if-else approach should be reserved for situation where a

-4-
switch cannot do the job (e.g., when the conditions involved are not simple equality expressions,
or when the case labels are not numeric constants).

3.3.Looping Statements
3.3.1. The ‘while’ Statement

The while statement (also called while loop) provides a way of repeating a statement while a
condition holds. It is one of the three flavors of iteration in C++. The general form of the while
statement is:
while (expression)
statement;

First expression (called the loop condition) is evaluated. If the outcome is nonzero then statement
(called the loop body) is executed and the whole process is repeated. Otherwise, the loop is
terminated.
For example, suppose we wish to calculate the sum of all numbers from 1 to some integer denoted
by n. This can be expressed as:

i = 1;
sum = 0;
while (i <= n)
sum += i;

For n set to 5, Table 2.9 provides a trace of the loop by listing the values of the variables involved
and the loop condition.

Table 5.1 While loop trace


Iteration i n i <= n sum += i++
First 1 5 1 1
Second 2 5 1 3
Third 3 5 1 6
Fourth 4 5 1 10
Fifth 5 5 1 15
Sixth 6 5 0

It is not unusual for a while loop to have an empty body (i.e., a null statement). The following
loop, for example, sets n to its greatest odd factor.

while (n % 2 == 0 && n /= 2)
;

-5-
Here the loop condition provides all the necessary computation, so there is no real need for a
body. The loop condition not only tests that n is even, it also divides n by two and ensures that the
loop will terminate should n be zero.

3.3.2. The ‘for’ Statement


The for statement (also called for loop) is similar to the while statement, but has two additional
components: an expression which is evaluated only once before everything else, and an
expression which is evaluated once at the end of each iteration. The general form of the for
statement is:
for (expression1; expression2; expression3)
statement;

First expression1 is evaluated. Each time round the loop, expression2 is evaluated. If the outcome is
nonzero then statement is executed and expression3 is evaluated. Otherwise, the loop is terminated.
The general for loop is equivalent to the following while loop:
expression1;
while (expression2) {
statement;
expression3;
}

The most common use of for loops is for situations where a variable is incremented or
decremented with every iteration of the loop. The following for loop, for example, calculates the
sum of all integers from 1 to n.
sum = 0;
for (i = 1; i <= n; ++i)
sum += i;

This is preferred to the while-loop version we saw earlier. In this example, i is usually called the
loop variable.
C++ allows the first expression in a for loop to be a variable definition. In the above loop, for
example, i can be defined inside the loop itself:
for (int i = 1; i <= n; ++i)
sum += i;

Contrary to what may appear, the scope for i is not the body of the loop, but the loop itself. Scope-
wise, the above is equivalent to:

-6-
int i;
for (i = 1; i <= n; ++i)
sum += i;

Any of the three expressions in a for loop may be empty. For example, removing the first and the
third expression gives us something identical to a while loop:
for (; i != 0;) // is equivalent to: while (i != 0)
something; // something;
Removing all the expressions gives us an infinite loop. This loop's condition is assumed to be
always true:
for (;;) // infinite loop
something;
For loops with multiple loop variables are not unusual. In such cases, the comma operator is used
to separate their expressions:
for (i = 0, j = 0; i + j < n; ++i, ++j)
something;
Because loops are statements, they can appear inside other loops. In other words, loops can be
nested. For example,
for (int i = 1; i <= 3; ++i)
for (int j = 1; j <= 3; ++j)
cout << '(' << i << ',' << j << ")\n";
produces the product of the set {1,2,3} with itself, giving the output:
(1,1)
(1,2)
(1,3)
(2,1)
(2,2)
(2,3)
(3,1)
(3,2)
(3,3)

3.3.3. The ‘do…while’ Statement


The do statement (also called do loop) is similar to the while statement, except that its body is
executed first and then the loop condition is examined. The general form of the do statement is:
do
statement;
while (expression);

First statement is executed and then expression is evaluated. If the outcome of the latter is nonzero
then the whole process is repeated. Otherwise, the loop is terminated.
The do loop is less frequently used than the while loop. It is useful for situations where we need
the loop body to be executed at least once, regardless of the loop condition. For example, suppose

-7-
we wish to repeatedly read a value and print its square, and stop when the value is zero. This can
be expressed as the following loop:
do {
cin >> n;
cout << n * n << '\n';
} while (n != 0);
Unlike the while loop, the do loop is never used in situations where it would have a null body.
Although a do loop with a null body would be equivalent to a similar while loop, the latter is
always preferred for its superior readability.

3.4.Other Statements
3.4.1. The ‘continue’ Statement
The continue statement terminates the current iteration of a loop and instead jumps to the next
iteration. It applies to the loop immediately enclosing the continue statement. It is an error to use
the continue statement outside a loop.
In while and do loops, the next iteration commences from the loop condition. In a for loop, the
next iteration commences from the loop’s third expression. For example, a loop which repeatedly
reads in a number, processes it but ignores negative numbers, and terminates when the number is
zero, may be expressed as:
do {
cin >> num;
if (num < 0) continue;
// process num here...
} while (num != 0);
This is equivalent to:
do {
cin >> num;
if (num >= 0) {
// process num here...
}
} while (num != 0);

A variant of this loop which reads in a number exactly n times (rather than until the number is
zero) may be expressed as:
for (i = 0; i < n; ++i) {
cin >> num;
if (num < 0) continue; // causes a jump to: ++i
// process num here...
}

-8-
When the continue statement appears inside nested loops, it applies to the loop immediately
enclosing it, and not to the outer loops. For example, in the following set of nested loops, the
continue applies to the for loop, and not the while loop:

while (more) {
for (i = 0; i < n; ++i) {
cin >> num;
if (num < 0) continue; // causes a jump to: ++i
// process num here...
}
//etc...
}

3.4.2. The ‘break’ Statement


A break statement may appear inside a loop (while, do, or for) or a switch statement. It causes a
jump out of these constructs, and hence terminates them. Like the continue statement, a break
statement only applies to the loop or switch immediately enclosing it. It is an error to use the
break statement outside a loop or a switch.
For example, suppose we wish to read in a user password, but would like to allow the user a
limited number of attempts:
for (i = 0; i < attempts; ++i) {
cout << "Please enter your password: ";
cin >> password;
if (Verify(password)) // check password for correctness
break; // drop out of the loop
cout << "Incorrect!\n";
}
Here we have assumed that there is a function called Verify which checks a password and returns
true if it is correct, and false otherwise.
Rewriting the loop without a break statement is always possible by using an additional logical
variable (verified) and adding it to the loop condition:
verified = 0;
for (i = 0; i < attempts && !verified; ++i) {
cout << "Please enter your password: ";
cin >> password;
verified = Verify(password));
if (!verified)
cout << "Incorrect!\n";
}
The break version is arguably simpler and therefore preferred.

3.4.3. The ‘goto’ Statement


The goto statement provides the lowest-level of jumping. It has the general form:
goto label;

-9-
where label is an identifier which marks the jump destination of goto. The label should be
followed by a colon and appear before a statement within the same function as the goto statement
itself. For example, the role of the break statement in the for loop in the previous section can be
emulated by a goto:
for (i = 0; i < attempts; ++i) {
cout << "Please enter your password: ";
cin >> password;
if (Verify(password)) // check password for correctness
goto out; // drop out of the loop
cout << "Incorrect!\n";
}
out:
//etc...
Because goto provides a free and unstructured form of jumping (unlike break and continue), it can
be easily misused. Most programmers these days avoid using it altogether in favor of clear
programming. Nevertheless, goto does have some legitimate (though rare) uses.

3.4.4. The ‘return’ Statement


The return statement enables a function to return a value to its caller. It has the general form:
return expression;
where expression denotes the value returned by the function. The type of this value should match
the return type of the function. For a function whose return type is void, expression should be
empty:
return;
The only function we have discussed so far is main, whose return type is always int. The return
value of main is what the program returns to the operating system when it completes its execution.
Under UNIX, for example, it its conventional to return 0 from main when the program executes
without errors. Otherwise, a non-zero error code is returned. For example:
int main (void)
{
cout << "Hello World\n";
return 0;
}
When a function has a non-void return value (as in the above example), failing to return a value
will result in a compiler warning. The actual return value will be undefined in this case (i.e., it will
be whatever value which happens to be in its corresponding memory location at the time).

- 10 -
Chapter Four
Array, Pointers and Strings

4. Arrays and Strings....................................................................................................... 2


4.1. Introduction......................................................................................................... 2
4.2. What is an arrays................................................................................................. 3
4.3. One Dimensional Array ...................................................................................... 3
4.3.1. Declaration of Arrays.................................................................................. 3
4.3.2. Accessing Array Elements.......................................................................... 4
4.3.3. Initialization of arrays ................................................................................. 6
4.3.4. Copying Arrays........................................................................................... 9
4.4. Multidimensional arrays ................................................................................... 10
4.4.1. Initializing Multidimensional Arrays........................................................ 11
4.4.2. Omitting the Array Size ............................................................................ 11
4.5. Strings representation and manipulation........................................................... 12
4.5.1. String Output............................................................................................. 13
4.5.2. String Input ............................................................................................... 13
4.5.3. Avoiding buffer over flow ........................................................................ 15
4.5.4. String constants......................................................................................... 15
4.5.5. Copying string the hard way ..................................................................... 15
4.5.6. Copying string the easy way ..................................................................... 16
4.5.7. Concatenating strings................................................................................ 17
4.5.8. Comparing strings..................................................................................... 18
4.6. Pointers ............................................................................................................. 19
4.6.1. Dynamic Memory ..................................................................................... 20
4.6.2. Pointer Arithmetic..................................................................................... 27
4.6.3. Function Pointers ...................................................................................... 28
4.6.4. References................................................................................................. 30
4.6.5. Typedefs.................................................................................................... 32

1
Chapter Four

4. Arrays and Strings

4.1. Introduction
Variables in a program have values associated with them. During program execution
these values are accessed by using the identifier associated with the variable in
expressions etc. In none of the programs written so far have very many variables been
used to represent the values that were required. Thus even though programs have been
written that could handle large lists of numbers it has not been necessary to use a separate
identifier for each number in the list. This is because in all these programs it has never
been necessary to keep a note of each number individually for later processing. For
example in summing the numbers in a list only one variable was used to hold the current
entered number which was added to the accumulated sum and was then overwritten by
the next number entered. If that value were required again later in the program there
would be no way of accessing it because the value has now been overwritten by the later
input.

If only a few values were involved a different identifier could be declared for each
variable, but now a loop could not be used to enter the values. Using a loop and assuming
that after a value has been entered and used no further use will be made of it allows the
following code to be written. This code enters six numbers and outputs their sum:

sum = 0.0;
for (i = 0; i < 6; i++)
{
cin >> x;
sum += x;
}
This of course is easily extended to n values where n can be as large as required.
However if it was required to access the values later the above would not be suitable. It
would be possible to do it as follows by setting up six individual variables:

float a, b, c, d, e, f;

and then handling each value individually as follows:

2
sum = 0.0;
cin >> a; sum += a;
cin >> b; sum += b;
cin >> c; sum += c;
cin >> d; sum += d;
cin >> e; sum += e;
cin >> f; sum += f;

which is obviously a very tedious way to program. To extend this solution so that it
would work with more than six values then more declarations would have to be added,
extra assignment statements added and the program re-compiled. If there were 10000
values imagine the tedium of typing the program (and making up variable names and
remembering which is which)!

To get round this difficulty all high-level programming languages use the concept of a
data structure called an Array.

4.2. What is an arrays


An array is a data structure which allows a collective name to be given to a group of
elements which all have the same type. An individual element of an array is identified by
its own unique index (or subscript).

An array can be thought of as a collection of numbered boxes each containing one data
item. The number associated with the box is the index of the item. To access a particular
item the index of the box associated with the item is used to access the appropriate box.
The index must be an integer and indicates the position of the element in the array. Thus
the elements of an array are ordered by the index.

4.3. One Dimensional Array


4.3.1. Declaration of Arrays
An array declaration is very similar to a variable declaration. First a type is given for the
elements of the array, then an identifier for the array and, within square brackets, the
number of elements in the array. The number of elements must be an integer.

For example data on the average temperature over the year in Ethiopia for each of the last
100 years could be stored in an array declared as follows:
float annual_temp[100];

3
This declaration will cause the compiler to allocate space for 100 consecutive float
variables in memory. The number of elements in an array must be fixed at compile time.
It is best to make the array size a constant and then, if required, the program can be
changed to handle a different size of array by changing the value of the constant,

const int NE = 100;


float annual_temp[NE];

then if more records come to light it is easy to amend the program to cope with more
values by changing the value of NE. This works because the compiler knows the value of
the constant NE at compile time and can allocate an appropriate amount of space for the
array. It would not work if an ordinary variable was used for the size in the array
declaration since at compile time the compiler would not know a value for it.

4.3.2. Accessing Array Elements


Given the declaration above of a 100-element array the compiler reserves space for 100
consecutive floating point values and accesses these values using an index/subscript that
takes values from 0 to 99. The first element in an array in C++ always has the index 0,
and if the array has n elements the last element will have the index n-1.

An array element is accessed by writing the identifier of the array followed by the
subscript in square brackets. Thus to set the 15th element of the array above to 1.5 the
following assignment is used:

annual_temp[14] = 1.5;

Note that since the first element is at index 0, then the ith element is at index i-1. Hence
in the above the 15th element has index 14.

An array element can be used anywhere an identifier may be used. Here are some
examples assuming the following declarations:
const int NE = 100,
N = 50;
int i, j, count[N];
float annual_temp[NE];
float sum, av1, av2;

A value can be read into an array element directly, using cin

cin >> count[i];


The element can be increased by 5,

4
count[i] = count[i] + 5;

or, using the shorthand form of the assignment

count[i] += 5;
Array elements can form part of the condition for an if statement, or indeed, for any other
logical expression:
if (annual_temp[j] < 10.0)
cout << "It was cold this year "
<< endl;
for statements are the usual means of accessing every element in an array. Here, the first
NE elements of the array annual_temp are given values from the input stream cin.
for (i = 0; i < NE; i++)
cin >> annual_temp[i];

The following code finds the average temperature recorded in the first ten elements of the
array.
sum = 0.0;
for (i = 0; i <10; i++)
sum += annual_temp[i];
av1 = sum / 10;

Notice that it is good practice to use named constants, rather than literal numbers such as
10. If the program is changed to take the average of the first 20 entries, then it all too easy
to forget to change a 10 to 20. If a const is used consistently, then changing its value will
be all that is necessary.

For example, the following example finds the average of the last k entries in the array. k
could either be a variable, or a declared constant. Observe that a change in the value of k
will still calculate the correct average (provided k<=NE).

sum = 0.0;
for (i = NE - k; i < NE; i++)
sum += annual_temp[i];
av2 = sum / k;
Important - C++ does not check that the subscript that is used to reference an array
element actually lies in the subscript range of the array. Thus C++ will allow the
assignment of a value to annual_temp[200], however the effect of this assignment is
unpredictable. For example it could lead to the program attempting to assign a value to a
memory element that is outside the program's allocated memory space. This would lead

5
to the program being terminated by the operating system. Alternatively it might actually
access a memory location that is within the allocated memory space of the program and
assign a value to that location, changing the value of the variable in your program which
is actually associated with that memory location, or overwriting the machine code of your
program. Similarly reading a value from annual_temp[200] might access a value that
has not been set by the program or might be the value of another variable. It is the
programmer's responsibility to ensure that if an array is declared with n elements then no
attempt is made to reference any element with a subscript outside the range 0 to n-1.
Using an index, or subscript, that is out of range is called Subscript Overflow. Subscript
overflow is one of the commonest causes of erroneous results and can frequently cause
very strange and hard to spot errors in programs.

4.3.3. Initialization of arrays


The initialization of simple variables in their declaration has already been covered. An
array can be initialized in a similar manner. In this case the initial values are given as a
list enclosed in curly brackets. For example initializing an array to hold the first few
prime numbers could be written as follows:
int primes[] = {1, 2, 3, 5, 7, 11, 13};

Note that the array has not been given a size, the compiler will make it large enough to
hold the number of elements in the list. In this case primes would be allocated space for
seven elements. If the array is given a size then this size must be greater than or equal to
the number of elements in the initialization list. For example:

int primes[10] = {1, 2, 3, 5, 7};


would reserve space for a ten element array but would only initialize the first five
elements.

Example Program: Printing Outliers in Data

The requirement specification for a program is:

A set of positive data values (200) are available. It is required to find the average value of
these values and to count the number of values that are more than 10% above the average
value.

6
Since the data values are all positive a negative value can be used as a sentinel to signal
the end of data entry. Obviously this is a problem in which an array must be used since
the values must first be entered to find the average and then each value must be compared
with this average. Hence the use of an array to store the entered values for later re-use.

An initial algorithmic description is:


initialize.
enter elements into array and sum elements.
evaluate average.
scan array and count number greater than
10% above average.
output results.
This can be expanded to the complete algorithmic description:
set sum to zero.
set count to zero.
set nogt10 to zero.
enter first value.
while value is positive
{
put value in array element with index count.
add value to sum.
increment count.
enter a value.
}
average = sum/count.
for index taking values 0 to count-1
if array[index] greater than 1.1*average
then increment nogt10.
output average, count and nogt10.

In the above the variable nogt10 is the number greater than 10% above the average value.
It is easy to argue that after exiting the while loop, count is set to the number of positive
numbers entered. Before entering the loop count is set to zero and the first number is
entered, that is count is one less than the number of numbers entered. Each time round the
loop another number is entered and count is incremented hence count remains one less
than the number of numbers entered. But the number of numbers entered is one greater
than the number of positive numbers so count is therefore equal to the number of positive
numbers.

A main() program written from the above algorithmic description is given below:

void main()
{
const int NE = 200; // maximum no of elements in array
float sum = 0.0; // accumulates sum
int count = 0; // number of elements entered
int nogt10 = 0; // counts no greater than 10%

7
// above average
float x; // holds each no as input
float indata[NE]; // array to hold input
float average; // average value of input values
int i; // control variable

// Data entry, accumulate sum and count


// number of +ve numbers entered
cout << "Enter numbers, -ve no to terminate: " << endl;
cin >> x;
while (x >= 0.0)
{
sum = sum + x;
indata[count] = x;
count = count + 1;
cin >> x;
}

// calculate average
average = sum/count;

// Now compare input elements with average


for (i = 0; i < count; i++)
{
if (indata[i] > 1.1 * average)
nogt10++;
}

// Output results
cout << "Number of values input is " << n;
cout << endl
<< "Number more than 10% above average is "
<< nogt10 << endl;
}

Since it was assumed in the specification that there would be less than 200 values the
array size is set at 200. In running the program less than 200 elements may be entered, if
n elements where n < 200 elements are entered then they will occupy the first n places in
the array indata. It is common to set an array size to a value that is the maximum we
think will occur in practice, though often not all this space will be used.

Example Program: Test of Random Numbers

The following program simulates the throwing of a dice by using a random number
generator to generate integers in the range 0 to 5. The user is asked to enter the number of
trials and the program outputs how many times each possible number occurred.

An array has been used to hold the six counts. This allows the program to increment the
correct count using one statement inside the loop rather than using a switch statement

8
with six cases to choose between variables if separate variables had been used for each
count. Also it is easy to change the number of sides on the dice by changing a constant.
Because C++ arrays start at subscript 0 the count for an i occurring on a throw is held in
the i-1th element of this count array. By changing the value of the constant die_sides the
program could be used to simulate a die_sides-sided die without any further change.
#include <iostream.h>
#include <stdlib.h> // time.h and stdlib.h required for
#include <time.h> // random number generation

void main()
{
const int die_sides = 6; // maxr-sided die
int count[die_sides]; // holds count of each
// possible value
int no_trials, // number of trials
roll, // random integer
i; // control variable
float sample; // random fraction 0 .. 1

// initialize random number generation and count


// array and input no of trials
srand(time(0));
for (i=0; i < die_sides; i++)
count[i] = 0;
cout << "How many trials? ";
cin >> no_trials;

// carry out trials


for (i = 0; i < no_trials; i++)
{
sample = rand()/float(RAND_MAX);
roll = int ( die_sides * sample);
// returns a random integer in 0 to die_sides-1
count[roll]++; // increment count
}

// Now output results


for (i = 0; i < die_sides; i++)
{
cout << endl << "Number of occurrences of "
<< (i+1) << " was " << count[i];
}
cout << endl;
}

4.3.4. Copying Arrays


The assignment operator cannot be applied to array variables:
const int SIZE=10
int x [SIZE] ;
int y [SIZE] ;

9
x = y ; // Error - Illegal
Only individual elements can be assigned to using the index operator, e.g., x[1] =
y[2];.
To make all elements in 'x' the same as those in 'y' (equivalent to assignment), a loop has
to be used.
// Loop to do copying, one element at a time
for (int i = 0 ; i < SIZE; i++)
x[i] = y[i];

This code will copy the elements of array y into x, overwriting the original contents of x.
A loop like this has to be written whenever an array assignment is needed.

Notice the use of a constant to store the array size. This avoids the literal constant '10'
appearing a number times in the code. If the code needs to be edited to use different sized
arrays, only the constant needs to be changed. If the constant is not used, all the '10's
would have to be changed individually - it is easy to miss one out.

4.4. Multidimensional arrays


An array may have more than one dimension. Each dimension is represented as a
subscript in the array. Therefore a two dimensional array has two subscripts, a three
dimensional array has three subscripts, and so on.

Arrays can have any number of dimensions, although most of the arrays that you create
will likely be of one or two dimensions.

A chess board is a good example of a two-dimensional array. One dimension represents


the eight rows, the other dimension represents the eight columns.

Suppose the program contains a class named square. The declaration of array named
board that represents would be

Square board[8][8];
The program could also represent the same data with a one dimensional, 64-square array.
For example, it could include the statement
Square board[64];
Such a representation does not correspond as closely to the real-world object as the two
dimensional array, however.

10
Suppose that when the game begins. The king id located in the fourth position in the first
row. Counting from zero that position corresponds to board[0][3] in the two dimensional
array, assuming that the first subscript corresponds to the row, and the second to the
column.

4.4.1. Initializing Multidimensional Arrays


To initialize a multidimensional arrays , you must assign the list of values to array
elements in order, with last array subscript changing while the first subscript while the
first subscript holds steady. Therefore, if the program has an array int
theArray[5][3], the first three elements go int theArray[0]; the next three
into theArray[1]; and so forth.

The program initializes this array by writing


int theArray[5][3] ={ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15};
for the sake of clarity, the program could group the initializations with braces, as shown
below.
int theArray[5][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9},
{10, 11, 12}, {13, 14,15} };
The compiler ignores the inner braces, which clarify how the numbers are distributed.
Each value should be separated by comma, regardless of whither inner braces are include.
The entire initialization must set must appear within braces, and it must end with a
semicolon.

4.4.2. Omitting the Array Size


If a one-dimensional array is initialized, the size can be omitted as it can be found from
the number of initializing elements:

int x[] = { 1, 2, 3, 4} ;
This initialization creates an array of four elements.
Note however:
int x[][] = { {1,2}, {3,4} } ; // error is not allowed.
and must be written
int x[2][2] = { {1,2}, {3,4} } ;

11
Example of multidimensional array
#include<iostream.h>
void main(){
int SomeArray[5][2] = {{0,0},{1,2}, {2,4},{3,6},
{4,8}}
for ( int i=0; i<5; i++)
for (int j = 0; j<2;j++)
{
cout<<"SomeArray["<<i<<"]["<<j<<'']: '';
cout<<endl<<SomeArray[i][ j];
}
}

4.5. Strings representation and manipulation


String in C++ is nothing but a sequence of character in which the last character is the null
character ‘\0’. The null character indicates the end of the string. Any array of character
can be converted into string type in C++ by appending this special character at the end of
the array sequence.

In C++ strings of characters are held as an array of characters, one character held in
each array element. In addition a special null character, represented by `\0', is appended
to the end of the string to indicate the end of the string. Hence if a string has n characters
then it requires an n+1 element array (at least) to store it. Thus the character `a' is stored
in a single byte, whereas the single-character string "a" is stored in two consecutive bytes
holding the character `a' and the null character.

A string variable s1 could be declared as follows:


char s1[10];

The string variable s1 could hold strings of length up to nine characters since space is
needed for the final null character. Strings can be initialized at the time of declaration just
as other variables are initialized. For example:
char s1[] = "example";
char s2[20] = "another example"
would store the two strings as follows:
s1 |e|x|a|m|p|l|e|\0|
s2 |a|n|o|t|h|e|r| |e|x|a|m|p|l|e|\0|?|?|?|?|
In the first case the array would be allocated space for eight characters, that is space for
the seven characters of the string and the null character. In the second case the string is
set by the declaration to be twenty characters long but only sixteen of these characters are
12
set, i.e. the fifteen characters of the string and the null character. Note that the length of a
string does not include the terminating null character.

4.5.1. String Output

A string is output by sending it to an output stream, for example:

cout << "The string s1 is " << s1 << endl;


would print
The string s1 is example
The setw(width) I/O manipulator can be used before outputting a string, the string will
then be output right-justified in the field width. If the field width is less than the length of
the string then the field width will be expanded to fit the string exactly. If the string is to
be left-justified in the field then the setiosflags manipulator with the argument
ios::left can be used.

4.5.2. String Input


When the input stream cin is used space characters, newline etc. are used as separators
and terminators. Thus when inputting numeric data cin skips over any leading spaces and
terminates reading a value when it finds a white-space character (space, tab, newline
etc. ). This same system is used for the input of strings, hence a string to be input cannot
start with leading spaces, also if it has a space character in the middle then input will be
terminated on that space character. The null character will be appended to the end of the
string in the character array by the stream functions. If the string s1 was initialized as in
the previous section, then the statement

cin << s1;


would set the string s1 as follows when the string "first" is entered (without the double
quotes)
|f|i|r|s|t|\0|e|\0|

Note that the last two elements are a relic of the initialization at declaration time. If the
string that is entered is longer than the space available for it in the character array then
C++ will just write over whatever space comes next in memory. This can cause some
very strange errors when some of your other variables reside in that space!

13
To read a string with several words in it using cin we have to call cin once for each
word. For example to read in a name in the form of a Christian name followed by a
surname we might use code as follows:

char christian[12], surname[12];


cout << "Enter name ";
cin >> christian;
cin >> surname;
cout << "The name entered was "
<< christian << " "
<< surname;
The name would just be typed by the user as, for example, Ian Aitchison and the
output would then be
The name entered was Ian Aitchison
Enter a string: Law is a bottomless pit.
You entered: Law
Where did the rest of the phrase go?
It turns the insertion operator >> consider a space to be a terminating character.

Thus it will read strings consisting of a single word, but anything typed after a space is
thrown away.

To read text containing blanks we use another function, cin::get().


#include<iostream.h>
void main()
{
const int max=80;
char str[max];
cout<<"\n Enter a string;";
cin.get(str,max); // max avoid buffer overflow
cout<<"\n You entered : "<<str;
}
Reading multiple lines

We have solved the problem of reading strings with embedded blanks, but what about
strings with multiple lines? It turns out that the cin::get() function can take a third
argument to help out in this situation.

This argument specifies the character that tells the function to stop reading. The default
value of this argument is the newline('\n')character, but if you call the function
with some other character for this argument, the default will be overridden by the
specified character.

14
In the next example, we call the function with a dollar sign ('$') as the third argument
//reads multiple lines, terminates on '$' character
#include<iostream.h>
void main(){
const int max=80;
char str[max];
cout<<"\n Enter a string:\n";
cin.get(str, max, '$'); //terminates with $
cout<<\n You entered:\n"<<str; }

now you can type as many lines of input as you want. The function will continue to
accept characters until you enter the terminated character $ (or untill you exceed the size
of the array. Remember, you must still press Enter key after typing the '$' character .

4.5.3. Avoiding buffer over flow


The strings in the program invites the user to type in a string. What happens if the user
enters a string that is longer than the array used to hold it? There is no built-in mechanism
in C++ to keep a program from inserting array elements outside an array.

However, it is possible to tell the >> operator to limit the number of characters it places
in an array.

//avoids buffer overflow with cin.width


#include<iostream.h>
#include<iomanip.h> //for setw
void main(){
const int MAX=20;
char str[MAX];
cout<<"\n Enter a string: ";
cin>>setw(MAX)>>str;
cout<<"\n You entered :"<<str;
}

4.5.4. String constants


You can initialize a string to a constant value when you define it. Here's an example'

#include<iostream.h>
void main(){
char str[] = "Welcome to C++ programming language";
cout<<str;
}

if you tried to the string program with strings that contain more than one word , you may
have unpleasant surprise. Copying string the hard way

15
The best way to understand the true nature of strings is to deal with them character by
character

#include<iostream.h>
#include<string.h> //for strlen()
void main()
{
const int max=80;
char str1[]='' Oh, Captain, my Captain!"
our fearful trip is done";
char str2[max];
for(int i=0; i<strlen(str1);i++)
str2[i]=str1[1];
str2[i]='\0';
cout<<endl;
cout<<str2;
}

4.5.5. Copying string the easy way


Ofcourse you don't need to use a for loop to copy a string. As you might have guesses, a
library function will do it for you. You can copy strings using strcpy or strncpy
function. We assign strings by using the string copy function strcpy. The prototype for
this function is in string.h.

strcpy(destination, source);

strcpy copies characters from the location specified by source to the location
specified by destination. It stops copying characters after it copies the terminating null
character.

o The return value is the value of the destination parameter.


You must make sure that the destination string is large enough to hold all of the
characters in the source string (including the terminating null character).

Example:
#include <iostream.h>
#include <string.h>
void main(){
char me[20] = "David";
cout << me << endl;
strcpy(me, "YouAreNotMe");
cout << me << endl ;
return;
}

16
There is also another function strncpy, is like strcpy, except that it copies only a
specified number of characters.
strncpy(destination, source, int n);

It may not copy the terminating null character.


Example
#include <iostream.h>
#include <string.h>
void main() {
char str1[] = "String test";
char str2[] = "Hello";
char one[10];
strncpy(one, str1, 9);
one[9] = '\0';
cout << one << endl;
strncpy(one, str2, 2);
cout << one << endl;
strcpy(one, str2);
cout << one << endl;
}

4.5.6. Concatenating strings


In C++ the + operator cannot normally be used to concatenate string, as it can in some
languages such as BASIC; that is you can't say
Str3 = str1 + str2;

You can use strcat() or strncat

The function strcat concatenates (appends) one string to the end of another string.

strcat(destination, source);
o The first character of the source string is copied to the location of the terminating null
character of the destination string.
o The destination string must have enough space to hold both strings and a terminating
null character.
Example:
#include <iostream.h>
#include <string.h>

void main() {
char str1[30];
strcpy(str1, "abc");
cout << str1 << endl;
strcat(str1, "def");
cout << str1 << endl;

char str2[] = "xyz";


strcat(str1, str2);

17
cout << str1 << endl;
str1[4] = '\0';
cout << str1 << endl;
}

The function strncat is like strcat except that it copies only a specified number of
characters.
strncat(destination, source, int n);
It may not copy the terminating null character.
Example:
#include <iostream.h>
#include <string.h>
void main() {
char str1[30];
strcpy(str1, "abc");
cout << str1 << endl;
strncat(str1, "def", 2);
str1[5] = '\0';
cout << str1 << endl;
char str2[] = "xyz";
strcat(str1, str2);
cout << str1 << endl;
str1[4] = '\0';
cout << str1 << endl;
}

4.5.7. Comparing strings


Strings can be compared using strcmp or strncmp functions
The function strcmp compares two strings.
strcmp(str1, str2);
strcmp returns: <0 if str1 is less than str2
=0 if str1 is equal to str2
>0 if str1 is greater than str2
Example:
#include <iostream.h>
#include <string.h>
void main() {
cout << strcmp("abc", "def") << endl;
cout << strcmp("def", "abc") << endl;
cout << strcmp("abc", "abc") << endl;
cout << strcmp("abc", "abcdef") << endl;
cout << strcmp("abc", "ABC") << endl;
}
The function strncmp is like strcmp except that it compares only a specified number
of characters.
strncmp(str1, str2, int n);

strncmp does not compare characters after a terminating null character has been found
in one of the strings.

18
Example:
#include <iostream.h>
#include <string.h>
void main()
{
cout << strncmp("abc", "def", 2) << endl;
cout << strncmp("abc", "abcdef", 3) << endl;
cout << strncmp("abc", "abcdef", 2) << endl;
cout << strncmp("abc", "abcdef", 5) << endl;
cout << strncmp("abc", "abcdef", 20) << endl;
}

4.6. Pointers
A pointer is simply the address of a memory location and provides an indirect way of
accessing data in memory. A pointer variable is defined to ‘point to’ data of a specific
type. For example:

int *ptr1; // pointer to an int


char *ptr2; // pointer to a char
The value of a pointer variable is the address to which it points. For example, given the
definitions

int num;
we can write:

ptr1 = &num;

The symbol & is the address operator; it takes a variable as argument and returns the
memory address of that variable. The effect of the above assignment is that the address of
num is assigned to ptr1. Therefore, we say that ptr1 points to num. Figure 5.Error!
Bookmark not defined. illustrates this diagrammatically.

ptr1 num
Figure: A simple integer pointer.
Given that ptr1 points to num, the expression

*ptr1

dereferences ptr1 to get to what it points to, and is therefore equivalent to num. The
symbol * is the dereference operator; it takes a pointer as argument and returns the
contents of the location to which it points.

19
In general, the type of a pointer must match the type of the data it is set to point to. A
pointer of type void*, however, will match any type. This is useful for defining pointers
which may point to data of different types, or whose type is originally unknown. A
pointer may be cast (type converted) to another type. For example,

ptr2 = (char*) ptr1;

converts ptr1 to char pointer before assigning it to ptr2.


Regardless of its type, a pointer may be assigned the value 0 (called the null pointer). The
null pointer is used for initializing pointers, and for marking the end of pointer-based data
structures (e.g., linked lists). 

4.6.1. Dynamic Memory


In addition to the program stack (which is used for storing global variables and stack
frames for function calls), another memory area, called the heap, is provided. The heap is
used for dynamically allocating memory blocks during program execution. As a result, it
is also called dynamic memory. Similarly, the program stack is also called static
memory.

Two operators are used for allocating and deallocating memory blocks on the heap. The
new operator takes a type as argument and allocated a memory block for an object of that
type. It returns a pointer to the allocated block. For example,

int *ptr = new int;


char *str = new char[10];
allocate, respectively, a block for storing a single integer and a block large enough for
storing an array of 10 characters.

Memory allocated from the heap does not obey the same scope rules as normal
variables. For example, in

void Foo (void)


{
char *str = new char[10];
//...
}

when Foo returns, the local variable str is destroyed, but the memory block
pointed to by str is not. The latter remains allocated until explicitly released
by the programmer.

20
The delete operator is used for releasing memory blocks allocated by new. It
takes a pointer as argument and releases the memory block to which it points. For
example:

delete ptr; // delete an object


delete [] str; // delete an array of objects

Note that when the block to be deleted is an array, an additional [] should be


included to indicate this.
Should delete be applied to a pointer which points to anything but a
dynamically-allocated object (e.g., a variable on the stack), a serious runtime
error may occur. It is harmless to apply delete to the 0 pointer.
Dynamic objects are useful for creating data which last beyond the
function call which creates them. Listing 5.1 illustrates this using a function
which takes a string parameter and returns a copy of the string.

Listing 5.1
1 #include <string.h>

2 char* CopyOf (const char *str)


3 {
4 char *copy = new char[strlen(str) + 1];

5 strcpy(copy, str);
6 return copy;
7 }

Annotation (analysis)
1 This is the standard string header file which declares a variety of
functions for manipulating strings.
4 The strlen function (declared in string.h) counts the characters in its
string argument up to (but excluding) the final null character. Because
the null character is not included in the count, we add 1 to the total and
allocate an array of characters of that size.
5 The strcpy function (declared in string.h) copies its second argument
to its first, character by character, including the final null character.

Because of the limited memory resources, there is always the possibility


that dynamic memory may be exhausted during program execution,
especially when many large blocks are allocated and none released. Should
new be unable to allocate a block of the requested size, it will return 0 instead.
It is the responsibility of the programmer to deal with such possibilities.
Chapter Four
Dynamic Memory Management
21
Introduction
After writing a C++ application, your application is given a certain amount of memory to
use, which is divided into three segments as follows:
o Code Segment, in which all the application code is stored
o Data Segment, that holds the global data
o Stack Segment, used as a container for local variables and other temporary
information.
In addition to these, the operating system also provides an extra amount of memory
called Heap also called free store.
The problem with local variables is that they don't persist: When the function returns, the
local variables are thrown away. Global variables solve that problem at the cost of
unrestricted access throughout the program, which leads to the creation of code that is
difficult to understand and maintain. Putting data in the free store solves both of these
problems.
You can think of the free store as a massive section of memory in which thousands of
sequentially numbered cells waiting for your data. You can't label these cells, though, as
you can with the stack. You must ask for the address of the cells that you reserve and
then store that address in a pointer.
The stack segment is cleaned automatically when a function returns. All the local
variables go out of scope, and they are removed from the stack. The free store is not
cleaned until your program ends, and it is your responsibility to free any memory that
you've reserved when you are done with it.
The advantage to the free store is that the memory you reserve remains available until
you explicitly free it. If you reserve memory on the free store while in a function, the
memory is still available when the function returns.
The advantage of accessing memory in this way, rather than using global variables, is that
only functions with access to the pointer have access to the data. This provides a tightly
controlled interface to that data, and it eliminates the problem of one function changing
that data in unexpected and unanticipated ways. For this to work, you must be able to
create a pointer to an area on the free store and to pass that pointer among functions. The
following sections describe how to do this.

22
new
You allocate memory on the free store in C++ by using the new keyword. new is followed
by the type of the object that you want to allocate so that the compiler knows how much
memory is required. Therefore, new int allocates two bytes in the free store, and new long
allocates four.
The return value from new is a memory address. It must be assigned to a pointer. To
create an int on the free store, you might write
int * pPointer;
pPointer = new int;
You can, of course, initialize the pointer at its creation with
int * pPointer = new int(10);
In either case, pPointer now points to an int on the free store. You can use this like any
other pointer to a variable and assign a value into that area of memory by writing
*pPointer = 72;
Or it can be written as
int * pPointer = new int (72);
This means, "Put 72 at the value in pPointer," or "Assign the value 72 to the area on the
free store to which pPointer points."
If new cannot create memory on the free store (memory is, after all, a limited resource) it
returns the null pointer. You must check your pointer for null each time you request new
memory.
WARNING: Each time you allocate memory using the new keyword, you must check to
make sure the pointer is not null.
delete
When you are finished with your area of memory, you must call delete on the pointer.
delete returns the memory to the free store. Remember that the pointer itself--as opposed
to the memory to which it points--is a local variable. When the function in which it is
declared returns, that pointer goes out of scope and is lost. The memory allocated with
new is not freed automatically, however. That memory becomes unavailable--a situation
called a memory leak. It's called a memory leak because that memory can't be recovered
until the program ends. It is as though the memory has leaked out of your computer.
To restore the memory to the free store, you use the keyword delete. For example,

23
delete pPointer;
When you delete the pointer, what you are really doing is freeing up the memory whose
address is stored in the pointer. You are saying, "Return to the free store the memory that
this pointer points to." The pointer is still a pointer, and it can be reassigned. The
following program demonstrates allocating a variable on the heap, using that variable,
and deleting it.
1: // Listing 8.4
2: // Allocating and deleting a pointer
3:
4: #include <iostream.h>
5: int main()
6: {
7: int localVariable = 5;
8: int * pLocal= &localVariable;
9: int * pHeap = new int;
10: if (pHeap == NULL)
11: {
12: cout << "Error! No memory for pHeap!!";
13: return 0;
14: }
15: *pHeap = 7;
16: cout << "localVariable: " << localVariable << "\n";
17: cout << "*pLocal: " << *pLocal << "\n";
18: cout << "*pHeap: " << *pHeap << "\n";
19: delete pHeap;
20: pHeap = new int;
21: if (pHeap == NULL)
22: {
23: cout << "Error! No memory for pHeap!!";
24: return 0;
25: }
26: *pHeap = 9;
27: cout << "*pHeap: " << *pHeap << "\n";
28: delete pHeap;
29: return 0;
30: }

Output: localVariable: 5
*pLocal: 5
*pHeap: 7
*pHeap: 9
Analysis: Line 7 declares and initializes a local variable. Line 8 declares and initializes a
pointer with the address of the local variable. Line 9 declares another pointer but

24
initializes it with the result obtained from calling new int. This allocates space on the free
store for an int. Line 10 verifies that memory was allocated and the pointer is valid (not
null). If no memory can be allocated, the pointer is null and an error message is printed.
To keep things simple, this error checking often won't be reproduced in future programs,
but you must include some sort of error checking in your own programs.
Line 15 assigns the value 7 to the newly allocated memory. Line 16 prints the value of the
local variable, and line 17 prints the value pointed to by pLocal. As expected, these are the
same. Line 19 prints the value pointed to by pHeap. It shows that the value assigned in line
15 is, in fact, accessible.
In line 19, the memory allocated in line 9 is returned to the free store by a call to delete.
This frees the memory and disassociates the pointer from that memory. pHeap is now free
to point to other memory. It is reassigned in lines 20 and 26, and line 27 prints the result.
Line 28 restores that memory to the free store.
Although line 28 is redundant (the end of the program would have returned that memory)
it is a good idea to free this memory explicitly. If the program changes or is extended, it
will be beneficial that this step was already taken care of.
WARNING: When you call delete on a pointer, the memory it points to is freed. Calling
delete on that pointer again will crash your program! When you delete a pointer, set it to
zero (null). Calling delete on a null pointer is guaranteed to be safe. For example:
Animal *pDog = new Animal;
delete pDog; //frees the memory
pDog = 0; //sets pointer to null //... delete pDog; //harmless
Another way you might inadvertently create a memory leak is by reassigning your
pointer before deleting the memory to which it points. Consider this code fragment:
1: float * pPointer = new float;
2: *pPointer = 72.0;
3: pPointer = new float;
4: *pPointer = 84.5;
Line 1 creates pPointer and assigns it the address of an area on the free store. Line 2 stores
the value 72 in that area of memory. Line 3 reassigns pPointer to another area of memory.
Line 4 places the value 84 in that area. The original area--in which the value 72 is now
held--is unavailable because the pointer to that area of memory has been reassigned.

25
There is no way to access that original area of memory, nor is there any way to free it
before the program ends.
The code should have been written like this:
1: float * pPointer = new float;
2: *pPointer = 72;
3: delete pPointer;
4: pPointer = new float;
5: *pPointer = 84;
Now the memory originally pointed to by pPointer is deleted, and thus freed, in line 3.
NOTE: For every time in your program that you call new, there should be a call to delete.
It is important to keep track of which pointer owns an area of memory and to ensure that
the memory is returned to the free store when you are done with it.
C++ also provides the possibility to allocate an entire array in the heap, keeping a pointer
to its first element. Again we use the new operator but mention the size of the array in
square brackets:
int *table;
table = new int[100];
This will allocate 100 * sizeof(int) = 100 * 4 = 400 bytes of memory and assign the
starting address of the memory block to the table pointer.
Arrays allocated in the heap are similar to those allocated in the data or stack segments,
so we may access an arbitrary element using the indexing operator []. For example the
next loop initializes each element of the array to zero:
For (int i = 0; i < 100; i++)
table[i]=0;
Bad news is C++ does not provide any method of initializing heap arrays as you would
an ordinary dynamic variable, so we have to do it ourselves using a loop similar to the
one above. The following line will generate errors at compilation :
table = new int[100](0);
To erase a heap-allocated array we will use the delete operator, but this time add a pair of
square brackets so that the compiler can differentiate it from an ordinary dynamic
variable.
delete [] table;

26
4.6.2. Pointer Arithmetic
In C++ one can add an integer quantity to or subtract an integer quantity from
a pointer. This is frequently used by programmers and is called pointer
arithmetic. Pointer arithmetic is not the same as integer arithmetic, because
the outcome depends on the size of the object pointed to. For example,
suppose that an int is represented by 4 bytes. Now, given
char *str = "HELLO";
int nums[] = {10, 20, 30, 40};
int *ptr = &nums[0]; // pointer to first element

str++ advances str by one char (i.e., one byte) so that it points to the second
character of "HELLO", whereas ptr++ advances ptr by one int (i.e., four
bytes) so that it points to the second element of nums. Figure 5.1 illustrates
this diagrammatically.

Figure 5.1 Pointer arithmetic.


H E L L O \0 10 20 30 40

str ptr

str++ ptr++

It follows, therefore, that the elements of "HELLO" can be referred to as


*str, *(str + 1), *(str + 2), etc. Similarly, the elements of nums can be
referred to as *ptr, *(ptr + 1), *(ptr + 2), and *(ptr + 3).
Another form of pointer arithmetic allowed in C++ involves subtracting
two pointers of the same type. For example:
int *ptr1 = &nums[1];
int *ptr2 = &nums[3];
int n = ptr2 - ptr1; // n becomes 2

Pointer arithmetic is very handy when processing the elements of an


array. Listing 5.2 shows as an example a string copying function similar to
strcpy.

Listing 5.2
1 void CopyString (char *dest, char *src)
2 {
3 while (*dest++ = *src++)
4 ;
5 }

Annotation
3 The condition of this loop assigns the contents of src to the contents of
dest and then increments both pointers. This condition becomes 0 when
the final null character of src is copied to dest.

27
In turns out that an array variable (such as nums) is itself the address of
the first element of the array it represents. Hence the elements of nums can
also be referred to using pointer arithmetic on nums, that is, nums[i] is
equivalent to *(nums + i). The difference between nums and ptr is that nums
is a constant, so it cannot be made to point to anything else, whereas ptr is a
variable and can be made to point to any other integer.
Listing 5.3 shows how the HighestTemp function (shown earlier in
Listing 5.Error! Bookmark not defined.) can be improved using pointer
arithmetic.
Listing 5.3
1 int HighestTemp (const int *temp, const int rows, const int columns)
2 {
3 int highest = 0;

4 for (register i = 0; i < rows; ++i)


5 for (register j = 0; j < columns; ++j)
6 if (*(temp + i * columns + j) > highest)
7 highest = *(temp + i * columns + j);
8 return highest;
9 }

Annotation
1 Instead of passing an array to the function, we pass an int pointer and
two additional parameters which specify the dimensions of the array. In
this way, the function is not restricted to a specific array size.
6 The expression *(temp + i * columns + j) is equivalent to
temp[i][j] in the previous version of this function.

HighestTemp can be simplified even further by treating temp as a one-


dimensional array of row * column integers. This is shown in Listing 5.4.

Listing 5.4
1 int HighestTemp (const int *temp, const int rows, const int columns)
2 {
3 int highest = 0;

4 for (register i = 0; i < rows * columns; ++i)


5 if (*(temp + i) > highest)
6 highest = *(temp + i);
7 return highest;
8 }

4.6.3. Function Pointers


It is possible to take the address of a function and store it in a function
pointer. The pointer can then be used to indirectly call the function. For
example,
int (*Compare)(const char*, const char*);

28
defines a function pointer named Compare which can hold the address of any
function that takes two constant character pointers as arguments and returns
an integer. The string comparison library function strcmp, for example, is
such. Therefore:
Compare = &strcmp; // Compare points to strcmp function

The & operator is not necessary and can be omitted:


Compare = strcmp; // Compare points to strcmp function

Alternatively, the pointer can be defined and initialized at once:


int (*Compare)(const char*, const char*) = strcmp;

When a function address is assigned to a function pointer, the two types


must match. The above definition is valid because strcmp has a matching
function prototype:
int strcmp(const char*, const char*);

Given the above definition of Compare, strcmp can be either called


directly, or indirectly via Compare. The following three calls are equivalent:
strcmp("Tom", "Tim"); // direct call
(*Compare)("Tom", "Tim"); // indirect call
Compare("Tom", "Tim"); // indirect call (abbreviated)

A common use of a function pointer is to pass it as an argument to


another function; typically because the latter requires different versions of the
former in different circumstances. A good example is a binary search function
for searching through a sorted array of strings. This function may use a
comparison function (such as strcmp) for comparing the search string against
the array strings. This might not be appropriate for all cases. For example,
strcmp is case-sensitive. If we wanted to do the search in a non-case-sensitive
manner then a different comparison function would be needed.
As shown in Listing 5.5, by making the comparison function a parameter
of the search function, we can make the latter independent of the former.

Listing 5.5

29
1 int BinSearch (char *item, char *table[], int n,
2 int (*Compare)(const char*, const char*))
3 {
4 int bot = 0;
5 int top = n - 1;
6 int mid, cmp;

7 while (bot <= top) {


8 mid = (bot + top) / 2;
9 if ((cmp = Compare(item,table[mid])) == 0)
10 return mid; // return item index
11 else if (cmp < 0)
12 top = mid - 1; // restrict search to lower half
13 else
14 bot = mid + 1; // restrict search to upper half
15 }
16 return -1; // not found
17 }

Annotation
1 Binary search is a well-known algorithm for searching through a sorted
list of items. The search list is denoted by table which is an array of
strings of dimension n. The search item is denoted by item.
2 Compare is the function pointer to be used for comparing item against the
array elements.
7 Each time round this loop, the search span is reduced by half. This is
repeated until the two ends of the search span (denoted by bot and top)
collide, or until a match is found.
9 The item is compared against the middle item of the array.
10 If item matches the middle item, the latter’s index is returned.
11 If item is less than the middle item, then the search is restricted to the
lower half of the array.
14 If item is greater than the middle item, then the search is restricted to the
upper half of the array.
16 Returns -1 to indicate that there was no matching item.
The following example shows how BinSearch may be called with
strcmp passed as the comparison function:

char *cities[] = {"Boston", "London", "Sydney", "Tokyo"};


cout << BinSearch("Sydney", cities, 4, strcmp) << '\n';

This will output 2 as expected. 

4.6.4. References
A reference introduces an alias for an object. The notation for defining
references is similar to that of pointers, except that & is used instead of *. For
example,

30
double num1 = 3.14;
double &num2 = num1; // num is a reference to num1

defines num2 as a reference to num1. After this definition num1 and num2 both
refer to the same object, as if they were the same variable. It should be
emphasized that a reference does not create a copy of an object, but merely a
symbolic alias for it. Hence, after
num1 = 0.16;

both num1 and num2 will denote the value 0.16.


A reference must always be initialized when it is defined: it should be an
alias for something. It would be illegal to define a reference and initialize it
later.
double &num3; // illegal: reference without an initializer
num3 = num1;

You can also initialize a reference to a constant. In this case a copy of the
constant is made (after any necessary type conversion) and the reference is set
to refer to the copy.
int &n = 1; // n refers to a copy of 1

The reason that n becomes a reference to a copy of 1 rather than 1 itself is


safety. Consider what could happen if this were not the case.
int &x = 1;
++x;
int y = x + 1;

The 1 in the first and the 1 in the third line are likely to be the same object
(most compilers do constant optimization and allocate both 1’s in the same
memory location). So although we expect y to be 3, it could turn out to be 4.
However, by forcing x to be a copy of 1, the compiler guarantees that the
object denoted by x will be different from both 1’s.
The most common use of references is for function parameters.
Reference parameters facilitates the pass-by-reference style of arguments, as
opposed to the pass-by-value style which we have used so far. To observe
the differences, consider the three swap functions in Listing 5.6.

Listing 5.6

31
1 void Swap1 (int x, int y) // pass-by-value (objects)
2 {
3 int temp = x;
4 x = y;
5 y = temp;
6 }

7 void Swap2 (int *x, int *y) // pass-by-value (pointers)


8 {
9 int temp = *x;
10 *x = *y;
11 *y = temp;
12 }

13 void Swap3 (int &x, int &y) // pass-by-reference


14 {
15 int temp = x;
16 x = y;
17 y = temp;
18 }
Annotation
1 Although Swap1 swaps x and y, this has no effect on the arguments
passed to the function, because Swap1 receives a copy of the arguments.
What happens to the copy does not affect the original.
7 Swap2 overcomes the problem of Swap1 by using pointer parameters
instead. By dereferencing the pointers, Swap2 gets to the original values
and swaps them.
13 Swap3 overcomes the problem of Swap1 by using reference parameters
instead. The parameters become aliases for the arguments passed to the
function and therefore swap them as intended.
Swap3 has the added advantage that its call syntax is the same as Swap1
and involves no addressing or dereferencing. The following main function
illustrates the differences:

int main (void)


{
int i = 10, j = 20;
Swap1(i, j); cout << i << ", " << j << '\n';
Swap2(&i, &j); cout << i << ", " << j << '\n';
Swap3(i, j); cout << i << ", " << j << '\n';
}

When run, it will produce the following output:


10, 20
20, 10
10, 20 

4.6.5. Typedefs
Typedef is a syntactic facility for introducing symbolic names for data types.
Just as a reference defines an alias for an object, a typedef defines an alias for

32
a type. Its main use is to simplify otherwise complicated type declarations as
an aid to improved readability. Here are a few examples:
typedef char *String;
Typedef char Name[12];
typedef unsigned int uint;

The effect of these definitions is that String becomes an alias for char*,
Name becomes an alias for an array of 12 chars, and uint becomes an alias
for unsigned int. Therefore:
String str; // is the same as: char *str;
Namename; // is the same as: char name[12];
uintn; // is the same as: unsigned int n;

The complicated declaration of Compare in Listing 5.5 is a good


candidate for typedef:
typedef int (*Compare)(const char*, const char*);

int BinSearch (char *item, char *table[], int n, Compare comp)


{
//...
if ((cmp = comp(item, table[mid])) == 0)
return mid;
//...
}

The typedef introduces Compare as a new type name for any function with the
given prototype. This makes BinSearch’s signature arguably simpler.

33
Chapter Three
Modular Programming

5.1. Definition of Functions ....................................................................................... 1


5.1.1. Declaring and Defining Functions .............................................................. 1
5.2. Scope of variables ............................................................................................... 6
5.2.1. Local Variables ........................................................................................... 6
5.2.2. Global Variables ......................................................................................... 8
5.3. Function Arguments.......................................................................................... 12
5.4. Passing arguments............................................................................................. 13
5.4.1. Pass by Value............................................................................................ 13
5.4.2. Pass by reference....................................................................................... 14
5.5. Return Values.................................................................................................... 16
5.6. Default Parameters............................................................................................ 18
5.7. Inline Functions ................................................................................................ 20
5.8. Recursive Functions.......................................................................................... 22
Modular programming and Modules
Modular programming is breaking down the design of a program into individual
components (modules) that can be programmed and tested independently. It is a
requirement for effective development and maintenance of large programs and projects .
With modular programming, procedures of a common functionality are grouped together
into separate modules. A program therefore no longer consists of only one single part. It is
now divided into several smaller parts which interact and which form the whole program.
5.1. Definition of Functions
Modules in C++ are called functions. A function is a subprogram that can act on data and
return a value. Every C++ program has at least one function, main(). When your program
starts, main() is called automatically. main() might call other functions, some of which might
call still others. Each function has its own name, and when that name is encountered, the
execution of the program branches to the body of that function. When the function returns,
execution resumes on the next line of the calling function. When a program calls a
function, execution switches to the function and then resumes at the line after the function
call. Well-designed functions perform a specific and easily understood task. Complicated
tasks should be broken down into multiple functions, and then each can be called in turn.
Functions come in two varieties: user-defined and built-in. Built-in functions are part of
your compiler package--they are supplied by the manufacturer for your use. In this chapter
we will discuss about user-defined functions.
5.1.1. Declaring and Defining Functions
Using functions in your program requires that you first declare the function and that you
then define the function. The declaration tells the compiler the name, return type, and
parameters of the function. The definition tells the compiler how the function works. No
function can be called from any other function that hasn't first been declared. The
declaration of a function is called its prototype.
5.1.1.1. Declaring the Function
There are three ways to declare a function:
 Write your prototype into a file, and then use the #include directive to include it in
your program.
 Write the prototype into the file in which your function is used.

-1-
 Define the function before it is called by any other function. When you do this, the
definition acts as its own declaration.
Although you can define the function before using it, and thus avoid the necessity of
creating a function prototype, this is not good programming practice for three reasons.
First, it is a bad idea to require that functions appear in a file in a particular order. Doing so
makes it hard to maintain the program as requirements change. Second, it is possible that
function A() needs to be able to call function B(), but function B() also needs to be able to
call function A() under some circumstances. It is not possible to define function A() before
you define function B() and also to define function B() before you define function A(), so at
least one of them must be declared in any case. Third, function prototypes are a good and
powerful debugging technique. If your prototype declares that your function takes a
particular set of parameters, or that it returns a particular type of value, and then your
function does not match the prototype, the compiler can flag your error instead of waiting
for it to show itself when you run the program.
Function Prototypes
Many of the built-in functions you use have their function prototypes already written in the
files you include in your program by using #include. For functions you write yourself, you
must include the prototype. The function prototype is a statement, which means it ends
with a semicolon. It consists of the function's return type, name, and parameter list. The
parameter list is a list of all the parameters and their types, separated by commas. Function
Prototype Syntax:
return_type function_name ( [type [parameterName1] type [parameterName2]]...);
The function prototype and the function definition must agree exactly about the return type,
the name, and the parameter list. If they do not agree, you will get a compile-time error.
Note, however, that the function prototype does not need to contain the names of the
parameters, just their types. A prototype that looks like this is perfectly legal:
long Area(int, int);
This prototype declares a function named Area() that returns a long and that has two
parameters, both integers. Although this is legal, it is not a good idea. Adding parameter
names makes your prototype clearer. The same function with named parameters might be
long Area(int length, int width);

-2-
It is now obvious what this function does and what the parameters are. Note that all
functions have a return type. If none is explicitly stated, the return type defaults to int. Your
programs will be easier to understand, however, if you explicitly declare the return type of
every function, including main().
Function Prototype Examples
long FindArea(long length, long width); // returns long, has two parameters
void PrintMessage(int messageNumber); // returns void, has one parameter
int GetChoice(); // returns int, has no parameters
BadFunction(); // returns int, has no parameters
Listing 5.1 demonstrates a program that includes a function prototype for the Area()
function.
Listing 5.1. A function declaration and the definition and use of that function.
1: // Listing 5.1 - demonstrates the use of function prototypes
2:
3: typedef unsigned short USHORT;
4: #include <iostream.h>
5: USHORT FindArea(USHORT length, USHORT width); //function prototype
6:
7: int main()
8: {
9: USHORT lengthOfYard;
10: USHORT widthOfYard;
11: USHORT areaOfYard;
12:
13: cout << "\nHow wide is your yard? ";
14: cin >> widthOfYard;
15: cout << "\nHow long is your yard? ";
16: cin >> lengthOfYard;
17:
18: areaOfYard= FindArea(lengthOfYard,widthOfYard);
19:
20: cout << "\nYour yard is ";
21: cout << areaOfYard;
22: cout << " square feet\n\n";
23: return 0;
24: }

-3-
25:
26: USHORT FindArea(USHORT l, USHORT w)
27: {
28: return l * w;
29: }
Output: How wide is your yard? 100
How long is your yard? 200
Your yard is 20000 square feet
Analysis: The prototype for the FindArea() function is on line 5. Compare the prototype with
the definition of the function on line 26. Note that the name, the return type, and the
parameter types are the same. If they were different, a compiler error would have been
generated. In fact, the only required difference is that the function prototype ends with a
semicolon and has no body. Also note that the parameter names in the prototype are length
and width, but the parameter names in the definition are l and w. The names in the prototype
are not used; they are there as information to the programmer. When they are included,
they should match the implementation when possible. This is a matter of good
programming style and reduces confusion, but it is not required, as you see here.
The arguments are passed in to the function in the order in which they are declared and
defined, but there is no matching of the names. Had you passed in widthOfYard, followed by
lengthOfYard, the FindArea() function would have used the value in widthOfYard for length and
lengthOfYard for width. The body of the function is always enclosed in braces, even when it
consists of only one statement, as in this case.
5.1.1.2. Defining the Function
The definition of a function consists of the function header and its body. The header is
exactly like the function prototype, except that the parameters must be named, and there is
no terminating semicolon. The body of the function is a set of statements enclosed in
braces. Function Definition Syntax
return_type function_name ( [type parameterName1], [type parameterName2]...)
{
statements;
}

-4-
A function prototype tells the compiler the return type, name, and parameter list. Func-
tions are not required to have parameters, and if they do, the prototype is not required to list
their names, only their types. A prototype always ends with a semicolon (;). A function
definition must agree in return type and parameter list with its prototype. It must provide
names for all the parameters, and the body of the function definition must be surrounded by
braces. All statements within the body of the function must be terminated with semicolons,
but the function itself is not ended with a semicolon; it ends with a closing brace. If the
function returns a value, it should end with a return statement, although return statements can
legally appear anywhere in the body of the function. Every function has a return type. If
one is not explicitly designated, the return type will be int. Be sure to give every function an
explicit return type. If a function does not return a value, its return type will be void.
Function Definition Examples
long Area(long l, long w)
{
return l * w;
}

void PrintMessage(int whichMsg)


{
if (whichMsg == 0)
cout << "Hello.\n";
if (whichMsg == 1)
cout << "Goodbye.\n";
if (whichMsg > 1)
cout << "I'm confused.\n";
}
Function Statements
There is virtually no limit to the number or types of statements that can be in a function
body. Although you can't define another function from within a function, you can call a
function, and of course main() does just that in nearly every C++ program. Functions can
even call themselves, which is discussed soon, in the section on recursion. Although there
is no limit to the size of a function in C++, well-designed functions tend to be small. Many
programmers advise keeping your functions short enough to fit on a single screen so that

-5-
you can see the entire function at one time. This is a rule of thumb, often broken by very
good programmers, but a smaller function is easier to understand and maintain. Each
function should carry out a single, easily understood task. If your functions start getting
large, look for places where you can divide them into component tasks.
Execution of Functions
When you call a function, execution begins with the first statement after the opening brace
({). Branching can be accomplished by using the if statement. Functions can also call other
functions and can even call themselves (see the section "Recursion," later in this chapter).
Each function has its own name, and when that name is encountered, the execution of the
program branches to the body of that function. When the function returns, execution
resumes on the next line of the calling function. This flow is illustrated in the following
figure.

5.2. Scope of variables


A variable has scope, which determines how long it is available to your program and where
it can be accessed. There are two scopes Local and Global.
5.2.1. Local Variables
Not only can you pass in variables to the function, but you also can declare variables within
the body of the function. This is done using local variables, so named because they exist
only locally within the function itself. When the function returns, the local variables are no
longer available. Local variables are defined like any other variables. The parameters
passed in to the function are also considered local variables and can be used exactly as if

-6-
they had been defined within the body of the function. Variables declared within the
function are said to have "local scope." That means that they are visible and usable only
within the function in which they are defined. In fact, in C++ you can define variables
anywhere within the function, not just at its top. The scope of the variable is the block in
which it is defined. Thus, if you define a variable inside a set of braces within the function,
that variable is available only within that block. Listing 5.2 is an example of using
parameters and locally defined variables within a function.
Listing 5.2. The use of local variables and parameters.
1: #include <iostream.h>
2:
3: float Convert(float);
4: int main()
5: {
6: float TempFer;
7: float TempCel;
8:
9: cout << "Please enter the temperature in Fahrenheit: ";
10: cin >> TempFer;
11: TempCel = Convert(TempFer);
12: cout << "\nHere's the temperature in Celsius: ";
13: cout << TempCel << endl;
14: return 0;
15: }
16:
17: float Convert(float TFer)
18: {
19: float TCel;
20: TCel = ((TFer - 32) * 5) / 9;
21: return TCel;
22: }
Output: Please enter the temperature in Fahrenheit: 212
Here's the temperature in Celsius: 100
Please enter the temperature in Fahrenheit: 32
Here's the temperature in Celsius: 0
Please enter the temperature in Fahrenheit: 85
Here's the temperature in Celsius: 29.4444
Analysis: On lines 6 and 7, two float variables are declared, one to hold the temperature in
Fahrenheit and one to hold the temperature in degrees Celsius. The user is prompted to
enter a Fahrenheit temperature on line 9, and that value is passed to the function Convert().

-7-
Execution jumps to the first line of the function Convert() on line 19, where a local variable,
named TCel, is declared. Note that this is local variable that exists only within the function
Convert(). The value passed as a parameter, TFer, is also just a local copy of the variable
passed in by main().The local function variable TCel is assigned the value that results from
subtracting 32 from the parameter TFer, multiplying by 5, and then dividing by 9. This
value is then returned as the return value of the function, and on line 11 it is assigned to the
variable TempCel in the main() function. The value is printed on line 13. The program is run
three times. The first time, the value 212 is passed in to ensure that the boiling point of
water in degrees Fahrenheit (212) generates the correct answer in degrees Celsius (100).
The second test is the freezing point of water. The third test is a random number chosen to
generate a fractional result.
Variables declared within a block are scoped to that block; they can be accessed only
within that block and "go out of existence" when that block ends. Global variables have
global scope and are available anywhere within your program. Normally scope is obvious,
but there are some tricky exceptions. Currently, variables declared within the header of a for
loop (for int i = 0; i<SomeValue; i++) are scoped to the block in which the for loop is created.
5.2.2. Global Variables
Variables defined outside of any function have global scope and thus are available from
any function in the program, including main(). Local variables with the same name as global
variables do not change the global variables. A local variable with the same name as a
global variable hides the global variable, however. If a function has a variable with the
same name as a global variable, the name refers to the local variable--not the global--when
used within the function. Listing 5.3 illustrates these points.
Listing 5.3. Demonstrating global and local variables.
1: #include <iostream.h>
2: void myFunction(); // prototype
3:
4: int x = 5, y = 7; // global variables
5: int main()
6: {
7:
8: cout << "x from main: " << x << "\n";
9: cout << "y from main: " << y << "\n\n";
10: myFunction();

-8-
11: cout << "Back from myFunction!\n\n";
12: cout << "x from main: " << x << "\n";
13: cout << "y from main: " << y << "\n";
14: return 0;
15: }
16:
17: void myFunction()
18: {
19: int y = 10;
20:
21: cout << "x from myFunction: " << x << "\n";
22: cout << "y from myFunction: " << y << "\n\n";
23: }
Output: x from main: 5
y from main: 7
x from myFunction: 5
y from myFunction: 10
Back from myFunction!
x from main: 5
y from main: 7
Analysis: This simple program illustrates a few key, and potentially confusing, points
about local and global variables. On line 1, two global variables, x and y, are declared. The
global variable x is initialized with the value 5, and the global variable y is initialized with
the value 7. On lines 8 and 9 in the function main(), these values are printed to the screen.
Note that the function main() defines neither variable; because they are global, they are
already available to main().
When myFunction() is called on line 10, program execution passes to line 18, and a local
variable, y, is defined and initialized with the value 10. On line 21, myFunction() prints the
value of the variable x, and the global variable x is used, just as it was in main(). On line 22,
however, when the variable name y is used, the local variable y is used, hiding the global
variable with the same name.The function call ends, and control returns to main(), which
again prints the values in the global variables. Note that the global variable y was totally
unaffected by the value assigned to myFunction()'s local y variable.

-9-
Listing 5.4. Variables scoped within a block.
1: // Listing 5.4 - demonstrates variables
2: // scoped within a block
3:
4: #include <iostream.h>
5:
6: void myFunc();
7:
8: int main()
9: {
10: int x = 5;
11: cout << "\nIn main x is: " << x;
12:
13: myFunc();
14:
15: cout << "\nBack in main, x is: " << x;
16: return 0;
17: }
18:
19: void myFunc()
20: {
21:
22: int x = 8;
23: cout << "\nIn myFunc, local x: " << x << endl;
24:
25: {
26: cout << "\nIn block in myFunc, x is: " << x;
27:
28: int x = 9;
29:
30: cout << "\nVery local x: " << x;
31: }
32:
33: cout << "\nOut of block, in myFunc, x: " << x << endl;
34: }
Output: In main x is: 5
In myFunc, local x: 8
In block in myFunc, x is: 8
Very local x: 9
Out of block, in myFunc, x: 8
Back in main, x is: 5

- 10 -
Analysis: This program begins with the initialization of a local variable, x, on line 10, in
main(). The printout on line 11 verifies that x was initialized with the value 5. MyFunc() is
called, and a local variable, also named x, is initialized with the value 8 on line 22. Its value
is printed on line 23. A block is started on line 25, and the variable x from the function is
printed again on line 26. A new variable also named x, but local to the block, is created on
line 28 and initialized with the value 9. The value of the newest variable x is printed on line
30. The local block ends on line 31, and the variable created on line 28 goes "out of scope"
and is no longer visible.
When x is printed on line 33, it is the x that was declared on line 22. This x was unaffected
by the x that was defined on line 28; its value is still 8. On line 34, MyFunc() goes out of
scope, and its local variable x becomes unavailable. Execution returns to line 15, and the
value of the local variable x, which was created on line 10, is printed. It was unaffected by
either of the variables defined in MyFunc(). Needless to say, this program would be far less
confusing if these three variables were given unique names!
Global Variables: A Word of Caution
In C++, global variables are legal, but they are almost never used. Globals are dangerous
because they are shared data, and one function can change a global variable in a way that is
invisible to another function. This can and does create bugs that are very difficult to find.
Scope resolution operator
When a local variable has the same name as a global variable, all references to the variable
name made with in the scope of the local variable. This situation is illustrated in the
following program.
# include<iostream.h>
float num = 42.8; //global variable
int main()
{
float num = 26.4; //local variable
cout<<”the value of num is:”<<num<<endl;
return 0;
}
The output of the above program is: the value of num is: 26.4

- 11 -
As shown by the above output, the local variable name takes precedence ovger the global
variable. In such cases, we can still access the global variable by using C++’s scope
resolution operator (::). This operator must be placed immediately before the variable
name, as in ::num. When used in this manner, the :: tells the compiler to use global
variable.E.g
float num = 42.8; //global variable
int main()
{
float num = 26.4; //local variable
cout<<”the value of num is:”<< ::num<<endl;
return 0;
}
The out is:
the value of the number is 42.8
As indicated the above output, the scope resolution operator causes the global, rather the
local variable to be accessed.
5.3. Function Arguments
Function arguments do not have to all be of the same type. It is perfectly reasonable to
write a function that takes an integer, two longs, and a character as its arguments. Any valid
C++ expression can be a function argument, including constants, mathematical and logical
expressions, and other functions that return a value.
Using Functions as Parameters to Functions
Although it is legal for one function to take as a parameter a second function that returns a
value, it can make for code that is hard to read and hard to debug. As an example, say you
have the functions double(), triple(), square(), and cube(), each of which returns a value. You
could write
Answer = (double(triple(square(cube(myValue)))));
This statement takes a variable, myValue, and passes it as an argument to the function cube(),
whose return value is passed as an argument to the function square(), whose return value is
in turn passed to triple(), and that return value is passed to double(). The return value of this
doubled, tripled, squared, and cubed number is now passed to Answer.

- 12 -
It is difficult to be certain what this code does (was the value tripled before or after it was
squared?), and if the answer is wrong it will be hard to figure out which function failed. An
alternative is to assign each step to its own intermediate variable:
unsigned long myValue = 2;
unsigned long cubed = cube(myValue); // cubed = 8
unsigned long squared = square(cubed); // squared = 64
unsigned long tripled = triple(squared); // tripled = 196
unsigned long Answer = double(tripled); // Answer = 392
Now each intermediate result can be examined, and the order of execution is explicit.
5.4. Passing arguments
5.4.1. Pass by Value
The arguments passed in to the function are local to the function. Changes made to the
arguments do not affect the values in the calling function. This is known as passing by
value, which means a local copy of each argument is made in the function. These local
copies are treated just like any other local variables. Listing 5.5 illustrates this point.
Listing 5.5. A demonstration of passing by value.
1: // Listing 5.5 - demonstrates passing by value
2:
3: #include <iostream.h>
4:
5: void swap(int x, int y);
6:
7: int main()
8: {
9: int x = 5, y = 10;
10:
11: cout << "Main. Before swap, x: " << x << " y: " <<y<< "\n";
12: swap(x,y);
13: cout << "Main. After swap, x: " << x << " y: " <<y << "\n";
14: return 0;
15: }
16:
17: void swap (int x, int y)
18: {
19: int temp;
20:
21: cout << "Swap. Before swap, x: " << x << " y: " <<y<< "\n";
22:
23: temp = x;

- 13 -
24: x = y;
25: y = temp;
26:
27: cout << "Swap. After swap, x: " << x << " y: " << y << "\n";
28:
29: }
Output: Main. Before swap, x: 5 y: 10
Swap. Before swap, x: 5 y: 10
Swap. After swap, x: 10 y: 5
Main. After swap, x: 5 y: 10
Analysis: This program initializes two variables in main() and then passes them to the swap()
function, which appears to swap them. When they are examined again in main(), however,
they are unchanged! The variables are initialized on line 9, and their values are displayed
on line 11. swap() is called, and the variables are passed in. Execution of the program
switches to the swap() function, where on line 21 the values are printed again. They are in
the same order as they were in main(), as expected. On lines 23 to 25 the values are
swapped, and this action is confirmed by the printout on line 27. Indeed, while in the swap()
function, the values are swapped. Execution then returns to line 13, back in main(), where
the values are no longer swapped.
As you've figured out, the values passed in to the swap() function are passed by value,
meaning that copies of the values are made that are local to swap(). These local variables are
swapped in lines 23 to 25, but the variables back in main() are unaffected.
5.4.2. Pass by reference
In C++, passing by reference is accomplished in two ways: using pointers and using
references. The syntax is different, but the net effect is the same. Rather than a copy being
created within the scope of the function, the actual original object is passed into the
function. Passing an object by reference allows the function to change the object being
referred to. In previous section showed that a call to the swap() function did not affect the
values in the calling function.

- 14 -
Listing 9.7. swap() rewritten with references.
1: //Listing 9.7 Demonstrates passing by reference
2: // using references!
3:
4: #include <iostream.h>
5:
6: void swap(int &x, int &y);
7:
8: int main()
9: {
10: int x = 5, y = 10;
11:
12: cout << "Main. Before swap, x: " << x << " y: " <<y<< "\n";
13: swap(x,y);
14: cout << "Main. After swap, x: " << x << " y: " <<y << "\n";
15: return 0;
16: }
17:
18: void swap (int &rx, int &ry)
19: {
20: int temp;
21:
22: cout << "Swap. Before swap, rx: " <<rx<< " ry: " <<ry<<"\n";
23:
24: temp = rx;
25: rx = ry;
26: ry = temp;
27:
28: cout << "Swap. After swap, rx: " <<rx<< " ry: " <<ry<< "\n";
29:
30: }
Output: Main. Before swap, x:5 y: 10
Swap. Before swap, rx:5 ry:10
Swap. After swap, rx:10 ry:5
Main. After swap, x:10, y:5
Anaylsis: Two variables are declared on line 10 and their values are printed on line 12. On
line 13, the function swap() is called, but note that x and y, not their addresses, are passed.
The calling function simply passes the variables.
When swap() is called, program execution jumps to line 18, where the variables are
identified as references. Their values are printed on line 22, but note that no special
operators are required. These are aliases for the original values, and can be used as such.

- 15 -
On lines 24-26, the values are swapped, and then they're printed on line 28. Program
execution jumps back to the calling function, and on line 14, the values are printed in
main(). Because the parameters to swap() are declared to be references, the values from main()
are passed by reference, and thus are changed in main() as well. References provide the
convenience and ease of use of normal variables.
5.5. Return Values
Functions return a value or return void. Void is a signal to the compiler that no value will be
returned. To return a value from a function, write the keyword return followed by the value
you want to return. The value might itself be an expression that returns a value. For
example:
return 5;
return (x > 5);
return (MyFunction());
These are all legal return statements, assuming that the function MyFunction() itself returns a
value. The value in the second statement, return (x > 5), will be zero if x is not greater than 5,
or it will be 1. What is returned is the value of the expression, 0 (false) or 1 (true), not the
value of x.
When the return keyword is encountered, the expression following return is returned as the
value of the function. Program execution returns immediately to the calling function, and
any statements following the return are not executed. It is legal to have more than one return
statement in a single function. Listing 5.6 illustrates this idea.
Listing 5.6. A demonstration of multiple return statements.
1: // Listing 5.6 - demonstrates multiple return
2: // statements
3:
4: #include <iostream.h>
5:
6: int Doubler(int AmountToDouble);
7:
8: int main()
9: {
10:
11: int result = 0;
12: int input;
13:
14: cout << "Enter a number between 0 and 10,000 to double: ";

- 16 -
15: cin >> input;
16:
17: cout << "\nBefore doubler is called... ";
18: cout<< "\ninput: " << input << " doubled: " <<result<<"\n";
19:
20: result = Doubler(input);
21:
22: cout << "\nBack from Doubler...\n";
23: cout<< "\ninput: " << input << " doubled: " <<result<<"\n";
24:
25:
26: return 0;
27: }
28:
29: int Doubler(int original)
30: {
31: if (original <= 10000)
32: return original * 2;
33: else
34: return -1;
35: cout << "You can't get here!\n";
36: }
Output: Enter a number between 0 and 10,000 to double: 9000
Before doubler is called...
input: 9000 doubled: 0
Back from doubler...
input: 9000 doubled: 18000
Enter a number between 0 and 10,000 to double: 11000
Before doubler is called...
input: 11000 doubled: 0
Back from doubler...
input: 11000 doubled: -1
Analysis: A number is requested on lines 14 and 15, and printed on line 18, along with the
local variable result. The function Doubler() is called on line 20, and the input value is
passed as a parameter. The result will be assigned to the local variable result, and the values
will be reprinted on lines 22 and 23. On line 31, in the function Doubler(), the parameter is
tested to see whether it is greater than 10,000. If it is not, the function returns twice the
original number. If it is greater than 10,000, the function returns -1 as an error value. The

- 17 -
statement on line 35 is never reached, because whether or not the value is greater than
10,000, the function returns before it gets to line 35, on either line 32 or line 34. A good
compiler will warn that this statement cannot be executed, and a good programmer will
take it out!
5.6. Default Parameters
For every parameter you declare in a function prototype and definition, the calling function
must pass in a value. The value passed in must be of the declared type. Thus, if you have a
function declared as
long myFunction(int);
the function must in fact take an integer variable. If the function definition differs, or if you
fail to pass in an integer, you will get a compiler error. The one exception to this rule is if
the function prototype declares a default value for the parameter. A default value is a value
to use if none is supplied. The preceding declaration could be rewritten as
long myFunction (int x = 50);
This prototype says, "myFunction() returns a long and takes an integer parameter. If an
argument is not supplied, use the default value of 50." Because parameter names are not
required in function prototypes, this declaration could have been written as
long myFunction (int = 50);
The function definition is not changed by declaring a default parameter. The function
definition header for this function would be
long myFunction (int x)
If the calling function did not include a parameter, the compiler would fill x with the
default value of 50. The name of the default parameter in the prototype need not be the
same as the name in the function header; the default value is assigned by position, not
name. Any or all of the function's parameters can be assigned default values. The one
restriction is this: If any of the parameters does not have a default value, no previous
parameter may have a default value.
If the function prototype looks like
long myFunction (int Param1, int Param2, int Param3);
you can assign a default value to Param2 only if you have assigned a default value to Param3.
You can assign a default value to Param1 only if you've assigned default values to both
Param2 and Param3. Listing 5.7 demonstrates the use of default values.

- 18 -
Listing 5.7. A demonstration of default parameter values.
1: // Listing 5.7 - demonstrates use
2: // of default parameter values
3:
4: #include <iostream.h>
5:
6: int AreaCube(int length, int width = 25, int height = 1);
7:
8: int main()
9: {
10: int length = 100;
11: int width = 50;
12: int height = 2;
13: int area;
14:
15: area = AreaCube(length, width, height);
16: cout << "First area equals: " << area << "\n";
17:
18: area = AreaCube(length, width);
19: cout << "Second time area equals: " << area << "\n";
20:
21: area = AreaCube(length);
22: cout << "Third time area equals: " << area << "\n";
23: return 0;
24: }
25:
26: AreaCube(int length, int width, int height)
27: {
28:
29: return (length * width * height);
30: }
Output: First area equals: 10000
Second time area equals: 5000
Third time area equals: 2500
Analysis: On line 6, the AreaCube() prototype specifies that the AreaCube() function takes
three integer parameters. The last two have default values. This function computes the area
of the cube whose dimensions are passed in. If no width is passed in, a width of 25 is used
and a height of 1 is used. If the width but not the height is passed in, a height of 1 is used. It is
not possible to pass in the height without passing in a width. On lines 10-12, the dimensions
length, height, and width are initialized, and they are passed to the AreaCube() function on line
15. The values are computed, and the result is printed on line 16. Execution returns to line

- 19 -
18, where AreaCube() is called again, but with no value for height. The default value is used,
and again the dimensions are computed and printed. Execution returns to line 21, and this
time neither the width nor the height is passed in. Execution branches for a third time to line
27. The default values are used. The area is computed and then printed.
DO remember that function parameters act as local variables within the function. DON'T
try to create a default value for a first parameter if there is no default value for the second.
DON'T forget that arguments passed by value can not affect the variables in the calling
function. DON'T forget that changes to a global variable in one function change that
variable for all functions.
5.7. Inline Functions
When you define a function, normally the compiler creates just one set of instructions in
memory. When you call the function, execution of the program jumps to those instructions,
and when the function returns, execution jumps back to the next line in the calling function.
If you call the function 10 times, your program jumps to the same set of instructions each
time. This means there is only one copy of the function, not 10.
There is some performance overhead in jumping in and out of functions. It turns out that
some functions are very small, just a line or two of code, and some efficiency can be gained
if the program can avoid making these jumps just to execute one or two instructions. When
programmers speak of efficiency, they usually mean speed: the program runs faster if the
function call can be avoided.
If a function is declared with the keyword inline, the compiler does not create a real
function: it copies the code from the inline function directly into the calling function. No
jump is made; it is just as if you had written the statements of the function right into the
calling function.
Syntax
inline return_type function_name ( [type parameterName1], [type
parameterName2]...)
{
statements;
}

- 20 -
Note that inline functions can bring a heavy cost. If the function is called 10 times, the
inline code is copied into the calling functions each of those 10 times. The tiny
improvement in speed you might achieve is more than swamped by the increase in size of
the executable program. What's the rule of thumb? If you have a small function, one or two
statements, it is a candidate for inline. When in doubt, though, leave it out. Listing 5.9
demonstrates an inline function.
Listing 5.9. Demonstrates an inline function.
1: // Listing 5.9 - demonstrates inline functions
2:
3: #include <iostream.h>
4:
5: inline int Double(int);
6:
7: int main()
8: {
9: int target;
10:
11: cout << "Enter a number to work with: ";
12: cin >> target;
13: cout << "\n";
14:
15: target = Double(target);
16: cout << "Target: " << target << endl;
17:
18: target = Double(target);
19: cout << "Target: " << target << endl;
20:
21:
22: target = Double(target);
23: cout << "Target: " << target << endl;
24: return 0;
25: }
26:
27: int Double(int target)
28: {
29: return 2*target;
30: }
Output: Enter a number to work with: 20
Target: 40
Target: 80
Target: 160

- 21 -
Analysis: On line 5, Double() is declared to be an inline function taking an int parameter and
returning an int. The declaration is just like any other prototype except that the keyword
inline is prepended just before the return value. This compiles into code that is the same as if
you had written the following:
target = 2 * target;
everywhere you entered
target = Double(target);
By the time your program executes, the instructions are already in place, compiled into the
OBJ file. This saves a jump in the execution of the code, at the cost of a larger program.
5.8. Recursive Functions
Most mathematical functions that we are familiar with are described by a simple formula.
For example, we can convert temperatures from Fahrenheit to Celsius by applying the
formula
C = 5 (F-32) / 9
Given this formula, it is trivial to write a C++ function; with declarations and braces
removed, the one line formula above translates into one line C++ statement. Mathematical
functions are sometimes defined in a less standard form. For example we can define a
function f, valid on non negative integers, that satisfies:
f(0) = 0
f(x) = 2, f(x-1) +x2 for x>0, x  Z
From this definition,
f(3) = 2f(2) + 32
= 2f(2) + 9 now we should compute f(2) first.
f(2) = 2f(1) + 22
= 2f(1) + 4 now we should compute f(1) first.
f(1) = 2f(0) + 12
= 2f(0) + 1 now we should compute f(0) first.
f(0) = 0
 f(1) = 2 (0) + 1
=0+1 =1

- 22 -
 f(2) = 2 (1) + 4
=2+4 =6
=> f(3) = 2 (6) + 9
= 12 + 9 = 21
A function that is defined in terms of it self is called recursive. f(0) in the above function is
the value which is the function directly known with out resorting to recursion. Such case is
known as the base case. A recursive function without a base case is meaningless. When
writing recursive routines, it is crucial to keep in mind the four basic rules of recursion.
These are:
1. Base case: you must always have some Base case, which can be solved with out
recursion.
2. Making progress: for the cases that are to be solved recursively, the recursive call
must always be to a case that makes progress to ward a base case.
3. Design rule: Assume that all recursive calls work without fail. In order to find f(n)
recursively you can assume f(n-k) will work (k  1)
4. Compound interest rule: never duplicate work by solving the same instance of a
problem in separate recursive rule.
Some examples of recursive problems
a) The factorial function f(n) = n!
f(n) can be defined as:
f(N) = N x (N-1) x (N-2) x (N-3) x ……..x 1 this is iterative defn
 Nx f(N - 1) for N  0
f (N )   this is called recursion.
1 for N  0
This function can be implemented both iteratively or recursively as
//iterative implementation
int factorial (int N)
{
int fact =1;
for(int i=1;i<=n; i++)
{
fact *=I;
}
return fact;
}

- 23 -
//recursive implementation
int factorial (int n)
{
if (n == 0) return 1;
return n*factorial(n-1)
}
b) Fibonacci progress :
is a sequence in which the ith term equals f(i) defined as
 f(i - 1)  f(i - 2) for i  2
f(i)  
1 for i  1 and 2
Some of the elements in the sequence are
1, 1, 2, 3, 5, 8, 13, 21, 34 …….
The function can be implemented both iteratively and recursively. The recursive
implementation is shown bellow.
int i_th_fibonacci_element (int n)
{
if (n == 1|| n==2) return 1;
else if n > 2
{
int val1 = i_th_fibonacci_element ( n-1);
int val2 = i_th_fibonacci_element ( n-2);
return (val1 + val2);
}
}
Some function cannot be implemented easily without recursion. Other functions,
which do admit both iterative and recursive solution are easier to understand in their
recursive form. So recursion is an essential tool for computer scientist.
However recursion is very expensive in terms of time complexity of a program so
that it is not generally advisable to use recursion to problems that is simple to
implement in iterative way. This is because of the time requirement to manipulate
stack at each function call.
Some more recursive problems and their implementation
C) The Euclidean algorithm
A positive integer d is a divisor of a larger integer n if n is some multiple of d; i.e. n
= k. d for some positive integer k

- 24 -
A positive integer is a common divisor of two larger integer n and m if it is a
divisor of both m & n. d is said to be greatest common divisor of two large integers
m& n, if it is a common divisor of m & n, and no other common divisor of m & n is
greater than d.
This can be denoted as d = gcd (m, n).
It might seem that finding gcd of large integers could be very tedious and time
consuming. But fortunately the ancient Greek scientist Euclid discovered a clever
recursive algorithm as follow.
 N if N  M

gcd (M, N)   gcd (N, M) if M  N
 gcd (N - M, M) if M  N

int gcd (int m, int n)
{
if (m == n) return n;
else if (m > n) return gcd (n, m);
else return gcd (n-m, m);
}
D) Binary Search
Binary search for an element x in sorted array A[] looks at the middle of the array. If that is
not x (the middle element of A ≠ x) then it continues the search in the half that potential
could hold x. Assuming the array is sorted in ascending order, the potential half array is the
left array if the middle is larger than x other wise the right the potential list to hold x. The
process contributes this recursive step until either x is found or the sub array is empty.
What should be returned in the index of the array that hold x if it founds or -1.
Implementation
int find (float*a, int start, int stop, float x )
{
if (start > stop) return -1;
int mid = (start + stop) / 2;
if (x == a[mid] return mid;
if (x < a[mid] return find (a, start, mid -1, x );
else return find (a, mid +1, start , x );
}
The call from other program to this function may look like

- 25 -
index = find(list, 0, size-1);
e) The tower of Hanoi
A classic example to demonstrate the power of recursion in problem solving is the tower of
Hanoi puzzle. In this puzzle we are given three pegs labeled A, B and C. Initially a tower
of N disk of different sizes is stacked on peg A in order of decreasing size. That is, the
largest disk is on the bottom, and the smallest disk is on the top of the tower as shown
bellow

A B C
The objective is to transfer the entire disks from peg A to one of the other pegs, say C
moving only one disk at one time, never moving the larger disk on the top of smaller disk.
The solution follows a simple procedure.
if N = 1 then just move it to destination peg. Otherwise
1- Reduce the problem that say move the top N-1 disk from peg A to B using C .
2- Move the Nth disk (which is the largest) from peg A to C.
3- Now move the N-1 disks from B to C using A. And this will tell us what to do for
any N.
The implementation is as follows:
#include <iostream.h>
#include <conio.h>
void Form(int N, char pegA ,char pegB , char pegC )
{
if (N==1)
cout<<"move top disk on peg "<<pegA<<" to peg "<<pegC<<endl;
else
{
Form(N-1,pegA,pegC,pegB);
cout<<"move top disk on peg "<<pegA<<" to peg "<<pegC<<endl;
Form(N-1,pegB,pegA,pegC);
}
}

- 26 -
int main ()
{
char F;
int N;
cout<<"How many disks ?=====>";cin>>N;
Form(N,'A','B','C') ;
getch () ;
return 0;
}

- 27 -
Chapter Six
6. Structures and File Management

6.1. Record: - Structure


Thus far you have worked with variable’s whose data types are very simple: they are a numbers of either integer
or floating-point format with a specific range. These types of variables, who only have a single value to their
name, are known as basic variables or primitives. Everything in computers is built on numbers, but not
necessarily singular types. Sometimes it’s advantageous to group common variables into a single collection. For
example a date would require a day, month, and year. From what we have discussed currently explained, you
could create three separate variables for each, like so:

int day, month, year;


This isn’t so bad, but what happens if you want to store two dates and not one? You’d have to create three
more variables and give them unique names:

int day1, month1, year1;


int day2, month1, year2;
This begins to become a hassle. Not only do you have to create many variables, but you have to keep giving
them unique names. C++ provides a way to collect similar variables into a single structure.

An array is a data structure which holds multiple numbers of objects having the same basic property (data
type) in a contiguous memory slots. Programmer can also reserve contiguous memory space for aggregates of
elements of arbitrary data types each. A data type which is created to reserve such type of memory space is
called user defined data type.

User defined data types can be equally used like predefined data types to declare variable identifiers. For
example we can define simple, array, or pointer variable identifier from this user defined data type.

6.1.1. Structure
The term structure in C++ means both a user-defined type which is a grouping of variables as well as meaning a
variable based on a user-defined structure type. For the purpose of distinction we will refer to the user-defined
type side as structure definition and the variable side as structure variable.

A structure definition is a user-defined variable type which is a grouping of one or more variables. The type
itself has a name, just like ‘int’, ‘double’, or ‘char’ but it is defined by the user and follows the normal rules of
identifiers. Once the type has been defined through the C++ ‘struct’ keyword, you can create variables from it
just like you would any other type.

Since a structure definition is a grouping of several types: it is a group of one or more variables. These are
known as elements or member variables as they are members of the structure definition they are part of.
Following through with our hinted example, a structure definition could be a ‘date’ which might be made up of
three ‘int’ member variables: ‘day’, ‘month’, and ‘year’.

Before creating a structure variable you must create a structure definition. This is a blue print for the compiler
that is used each time you create a structure variable of this type. The structure definition is a listing of all
member variables with their types and names.
When you create a structure variable based on a structure definition, all of the member variables names are
retained. The only name you have to give is that of the new structure variable. The element names within that
variable will be the same as in the structure type. If you create two structure variables from ‘date’, both will
have all three member variables with the same name in both: ‘day’, ‘month’, and ‘year’.

Member variables are distinguished by the structure variable they are part of. You wouldn’t simply be able to
use ‘day’ by itself; instead you’d have to refer to both the structure variable’s name (that you gave when you
created it) as well as ‘day’.

6.1.2. struct Specification: Defining Structures


Defining a structure is giving the compiler a blue print for creating your type. When you create a variable based
on the structure definition, all of the member variables are created automatically and grouped under the name
you gave. Note that when you create a variable of any kind, you must give it a unique name that is different than
its type. Below is the syntax for a structure definition:

struct structname
{
datatype1 variable1;
datatype2 variable2;

};
Discussion: Writing a structure definition begins with the word ‘struct’ followed by the type-to-be, and ended
with a structure block that is ultimately terminated with a semicolon. Do not forget this
trailing semi-colon when defining a structure!

The name of a structure definition is known as the structure tag. This will be the name of the type that you
create, like ‘int’ or ‘float’. It is the type that you will specify when creating a structure variable. This structure
block is remarkably similar to a statement block since it starts and ends with curly braces. But don’t forget that
it ultimately ends with a semi-colon. Within the structure block you declare all the member variables you want
associated with that type. Declare them as you would normal variables, but do not try to initialize them. This
is simply a data blue print, it is not logic or instructions and the compiler does not execute it.

The data members (synonym for member variables) of a structure won’t actually be created until a variable based
on the structure is created. Technically an ‘int’ is just this as well. It’s a description of a storage unit. That
storage unit isn’t reserved until you create a variable with it.

Example defining a student struct,

struct student
{
int id;
char name[15];
};

Example: The follow defines a structure called ‘date’ which contains three ‘int’ member variables: ‘day’, ‘month’,
and ‘year’:

struct date {
int day;
int month;
int year;
};
struct date
{
int day, month, year;
};
Note: You cannot initialize member variables in a structure definition. The following is wrong and will
not compile:
struct date{
int day = 24, month = 10, year = 2001;
};
A structure definition has the same type of scoping as a variable. If you define a structure in a function, you will
only be able to use it there. If you define it in a nested statement block, you will only be able to use it inside
there and any statement blocks nested within it. But the most common place is defining it globally, as in
outside of any functions or blocks. Typically, you’ll want to be able to create variables of the defined structure
anywhere in your program, so the definition will go at the top. The following is an empty program that defines a
‘date’ structure globally:

#include <iostream.h>

struct date{
int day, month, year;
};
int main(){
return 0;
}

6.1.3. Declaring and using sturct data types


Once you have defined a structure you can create a variable from it just as you would any other variable.

student std1;
date birthday;
The above declaration statements would create a variable called ‘birthday’ whose type is the structure ‘date’.
The variable contains three parts: ‘day’, ‘month’, and ‘year’. What this actually does is set aside a whole block of
memory that can contain all of the member variables. Each member variable then occupies a chunk of it for
their individual storage units. The member variables are not actually created one at a time.

Storage for member variables exist at some offset from the beginning of the glob of memory reserved by the
entire structure. For example, in our ‘date’ structure variable the first member variable is ‘day’ so it exists at
offset 0. The next member variable, ‘month’ in this case, will exist at the next available offset. If ‘day’ uses 4
bytes (32 bits), then ‘month’ will be at offset 4:

int i;
student std1;
The above statements are similar in nature because both declare a variable of a given type. The former is a built
in type, which is integer while the later declares a variable type of user-defined type. std1 will have the
characteristics of representing id and name of a student.
6.1.3.1. Initializing Structure Variables
You cannot initialize member variables in the structure definition. This is because that definition is only a map,
or plan, of what a variable based on this type will be made of. You can, however, initialize the member variables
of a structure variable. That is, when you create a variable based on your structure definition you can pass each
member variable an initializer.

To initialize a structure variable’s members, you follow the original declaration with the assignment operator
(=). Next you define an initialization block which is a list of initializers separated by commas and enclosed in
curly braces. Lastly, you end it with a semi-colon. These values are assigned to member variables in the order
that they occur. Let’s look at an example:

date nco_birthday = { 19, 8, 1979 };


student std1={"Ababe", "Scr/2222/22"};
This creates a variable called ‘nco_birthday’ and initializes it to a list of values. The values are assigned to the
member variables in the order they are declared in the structure definition. Remember what is mentioned
about each member variable having an offset. The same order in which each member is given an offset is the
order in which each is assigned a value in an initialization. So the first initializer is used to initialize the first
member variable in the structure, next is the second, and so on and so forth. This order of initialization
continues until the values run out.

If you try to assign more values than are member variables, you will get a compiler error. However, you can
assign fewer values than there are member variables. If there are no more values to be assigned, the assignment
will simply end. For example, if we had omitted the last value, ‘1979’, then no value would be assigned to ‘year’.

It is possible to use any expression that you normally would. But remember that the expression must result in a
value. Here is an example of initialization with things other than literals:

int myday = 19;


int mymonth = 5;
date nco_birthday = { myday, mymonth + 3, 2001 - 22 };
Although you can assign a value to a variable in the same way you initialize it, the same is not true with
structures. So while this works:

int x;
x = 0;
This doesn’t:

date nco_birthday;
nco_birthday = { 19, 8, 1979 };
Assigning values to multiple members of a structure variable is only possible when that variable is first created.
Once a structure variable has been declared, you must access each member individually.

6.1.3.2. Accessing members of a structure variable


There’s not much you can do with the structure itself; much of your time with structure variables will be spent
using its members. You can use a member variable in any place you’d use a normal variable, but you must
specify it by the structure variable’s name as well as the member variable’s name using the member operator.
To specify that you want a member of a specific structure variable, you use the structure member operator
which is the period (also known as a “dot”). Simply use the structure’s name, follow with the period, and end
with the member:

structure.member
Example: to reading and displaying values to and from structure s1.
cin>>s1.id; //storing to id item of s1
cin>>s1.name; //storing a name to s1
cout<<s1.id; //displaying the content of id of s1.
cout<<s1.name; //displaying name

Example:-a program that creates student struct and uses it to store student information.
#include<iostream.h>
#include<conio.h>
struct student
{
int id;
char name[15];
};

void main()
{
//creating three student variables
student s1,s2;
cout<<"\n Enter Student Id";
cin>>s1.id;
cout<<"\nEnter Name";
cin>>s1.name;
cout<<"\n Enter Student Id";
cin>>s2.id;
cout<<"\nEnter Name";
cin>>s2.name;

cout<<"\nStudents Information";
cout<<"\n Student id\t Student Name";
cout<<endl<<s1.id<<"\t"<<s1.name;
cout<<endl<<s2.id<<"\t"<<s2.name;
getch();
}
The above program shows you how to capture data in student variables.
Example 2: This program demonstrates using member variables for user input, output, and mathematical
operations.
#include <iostream.h>
struct date
{
int day, month, year;
};
int main()
{
date birth;
cout << “Enter your birth date!” << endl;
cout << “Year: “;
cin >> birth.year;
cout << “Month: “;
cin >> birth.month;
cout << “Day: “;
cin >> birth.day;
cout << “You entered “ << birth.month << “/”<< birth.day
<< “/” << birth.year << endl;
cout << “You were born in the “<< ((birth.year / 100) + 1)
<< “th Century!”<< endl;
return 0;
}

6.1.3.3. Variables with Definition


The syntax of ‘struct’ also allows you to create variables based on a structure definition without using two
separate statements:

struct tag
{
member(s);
} variable;
The structure variables created in this way will have the same scope as their structure definition. This is a nice
thing to use when you want to group some variables in one place in your program without it affecting other
things. You can create a structure definition as well as variables from it in that one local place and not have to
ever use it again.
Example: A ‘point’ variable right after the ‘pointtag’ structure is defined:
struct pointtag{
int x, y;
} point;
In this, ‘point’ is a variable just as if we had declared it separately. In fact, this statement is identical to:
struct pointtag{
int x, y;
};
pointtag point;

Rather than defining a structure under a name and then creating variables which refer to the named definition,
the definition becomes part of the variable declaration. It is even possible to omit the structure tag which may
make more sense in this situation:
struct{
int x, y;
} point;
The above creates a structure variable called ‘point’ which has two member variables. Because the structure
definition is not named, it cannot be used elsewhere to create variables of the same structure type. However,
like in any variable declaration, you can create multiple variables of the same type by separating them with
commas:
struct{
int x, y;
} point1, point2;

Now we have two structure variables of the same nameless structure type. Even more fun we can initialize
these structure variables as we normally would:

struct{
int x, y;
} point1 = { 0, 0}, point2 = {0, 0};

This creates the two structure variables, ‘point1’ and ‘point2’, and initializes both members, ‘x’ and ‘y’, on each
to zero (0). You don’t need to make a name-less definition to do this. You could still have a structure tag and
initialize the variables created after the definition.
Note: Not only can you write name-less structures, but you can write them without declaring any variables.
This will cause a warning on smarter compilers, but is perfectly legal:
struct
{
int x, y;
};

The above structure definition can’t be used to create any variables because it has no name. Likewise, no
variables are declared after it either. So this is a nice way to fill up your programs with useless source code. But
why stop there? You could make an empty, name-less definition:
struct { };

6.1.4. Array of structs


#include<iostream.h>
#include<conio.h>
struct student {
int id;
char name[15];
};

void main()
{
clrscr();
//creating 5 student using an array
student s[5];
int i;
for(i=0; i < 5; i ++)
{
cout<<"\n Enter Student Id";
cin>>s[i].id;
cout<<"\nEnter Name";
cin>>s[i].name;
}
cout<<"\n Displaying student Info";
cout<<"\nStudent Id \t Student Name";
cout<<"\n============================";

for( i = 0; i < 5; i++)


cout<<endl<<s[i].id<<"\t\t\t"<<s[i].name;

getch(); }
Memory map of the above struct declaration.
0 id 1
name Tameru
1 id 2
name Hassen
2 id 3
name Selamawit
3 id 4
name Asia
4 id 5
name Micheal

If you use s[0].id you will be referring to 1, and s[0].name will refer to Tameru.
The following program declares and uses a book struct. Also swaps the content of two book struct variables.
#include<iostream.h>
#include<conio.h>
struct Book {
int id;
char title[15];
};
void main(){
//creating three Book variables
Book b1,b2,temp;
cout<<"\n Enter Book Id";
cin>>b1.id;
cout<<"\nEnter Title";
cin>>b1.title;
cout<<"\n Enter Book Id";
cin>>b2.id;
cout<<"\nEnter Title";
cin>>b2.title;
cout<<"\n Book Information";
cout<<"\n Before Changing Contents";
cout<<"\n Book id\t Title";
cout<<"\n=========================";
cout<<endl<<b1.id<<"\t\t\t"<<b1.title;
cout<<endl<<b2.id<<"\t\t\t"<<b2.title;
//swapping content
temp=b1;
b1=b2;
b2=temp;
cout<<"\nAfter swapping contents";
cout<<"\n Book Information";
cout<<"\n Book id\t Title";
cout<<"\n=========================";

cout<<endl<<b1.id<<"\t"<<b1.title;
cout<<endl<<b2.id<<"\t"<<b2.title;
getch();
}

6.1.5. Declaring struct types as part of a struct


A structure definition contains multiple variables, but not necessarily just primitives. You can define a
structure to have structure member variables.
Now if you have data's like birth of day of an employee, published year of a book, address of a person. What are
you going to do? You must be able to incorporate this type of data's in other structs. The following program
declares two structs one for address and other for student.
#include<iostream.h>
#include<conio.h>
struct Address{
int kebele;
char Kefle_ketema[20];
char roadname[20];
};
struct Student {
int id;
char name[15];
char section[6];
//declaring address type within student
Address studaddress;
};
void main(){
clrscr();
//creating Student type that encapsulates Address
Student s1;
cout<<"\n Enter Student Id";
cin>>s1.id;
cout<<"\nEnter Student Name";
cin>>s1.name;
cout<<"\n Enter Section";
cin>>s1.section;
//reading address attributes
cout<<"\nEnter Kebele";
cin>>s1.studaddress.kebele;
cout<<"\nEnter Street Name";
cin>>s1.studaddress.roadname;
cout<<"\nEnter Kefle Ketema";
cin>>s1.studaddress.Kefle_ketema;
cout<<"\n Student Information";
cout<<"\n id\t Name\t Section \t Kebele";
cout<<" \t Street Name \t Kefele Ketema ";
cout<<"\n==================================";
cout<<endl<<s1.id<<"\t"<<s1.name;
cout<<"\t"<<s1.section<<"\t"<<s1.studaddress.kebele;
cout<<"\t"<<s1.studaddress.roadname<<"\t";
cout<<s1.studaddress.Kefle_ketema;
getch();
}

The memory map of student s;


id 1
name Asefa
section RDDOB02
studaddress kebele 21
Kefle_ketema Arada
roadname Gahandi
Using the above figure. You should be able to understand how data is accessed. Example s.id refers id of
student, but s.studadress.kebele refers kebele of student where the kebele is referenced by studaddress, intern
part of student.
Example: This assumes that ‘date’ and ‘time’ are structures that have already been declared.
struct moment
{
date theDate;
time theTime;
};
These structure members can then be accessed as normal member variables, but then their variables must be
accessed as well. If there is a variable based on ‘moment’ called ‘birth’, then we would need to write the
following to assign ‘19’ to “birth’s date’s day”:
birth.theDate.day = 19;
A structure only has to be declared before it can be used in another structure’s definition. The following is
perfectly acceptable:
struct date;
struct time;
struct moment
{
date theDate;
time theTime;
};
struct date
{
int day, month, year;
};
struct time
{
int sec, min, hour;
};

To be able to define a structure you only must know the types and the names of the member variables declared
inside. With the above we declare the structures ‘date’ and ‘time’ but do not define them until later. This
simply acknowledges that they exist and they can therefore be used within ‘moment’.
What if ‘date’ and ‘time’ hadn’t defined? It would still be legal, but then I would not be able to use ‘moment’ at
all. Why? Since ‘date’ or ‘time’ have not been defined, the compiler does not know how big they are supposed
to be or what kind of data they contain. You couldn’t then create a variable based on ‘moment’ because the
compiler doesn’t know how big of a memory block to allocate. Likewise if you try to use a structure that has
been declared before it has been defined, you will encounter the same problem.

6.1.6. Defining Structure in Structure


It is possible to define a structure inside a structure definition and create variables from it at the same time. For
example:

struct moment
{
struct date
{
int day, month, year;
} theDate;

struct time
{
int second, minute, hour;
} theTime;
};

The drawback of the above is that the ‘date’ and ‘time’ definitions cannot be used elsewhere without also
referring to the parent structure. If the ‘date’ definition isn’t going to be used elsewhere anyway, the structure
tag can simply be omitted. Thus we could remove the ‘date’ and ‘time’ structure type identifiers and it would
work fine. You can write any number of variables in a structure definition and a valid variable declaration
statement (minus initialization of course) is valid inside a structure definition.
Let’s say we want to be able to use ‘date’ elsewhere, but not ‘time’. The following program demonstrates how
the structure definitions would be written as well as uses the defined structure types in an extended “birth
date” sample:
#include <iostream.h>
struct date { int day, month, year; } ;
struct moment
{
date theDate;
struct
{
int sec, min, hour;
} theTime;
};
int main()
{
moment birth;
cout << “Enter your birth moment!” << endl;
cout << “Year: “;
cin >> birth.theDate.year;
cout << “Month: “;
cin >> birth.theDate.month;
cout << “Day: “;
cin >> birth.theDate.day;
cout << “Hour (military): “;
cin >> birth.theTime.hour;
cout << “Minute: “;
cin >> birth.theTime.min;
cout << “Second: “;
cin >> birth.theTime.sec;

cout << “You entered “ << birth.theDate.month << “/”


<< birth.theDate.day << “/” << birth.theDate.year
<< “ @ “ << birth.theDate.hour << “:”
<< birth.theDate.min << “:” << birth.theDate.sec
<< endl;

if (birth.theTime.hour > 20 || birth.theTime.hour < 8)


cout << “You were born early in the morning!”
<< endl;

return 0;
}

Any number of structure definitions can be nested; you can get extremely complex with structures, which is
why they are sometimes known as complex types.

6.1.7. Structure, Reference and Pointer


References to structure variables, work the same way as normal references. To create one you would write out
the name of the structure type, followed by the reference name which is preceded by the reference operator
(&):
struct_name &reference;

The following creates a structure variable based on ‘date’ and a reference to it:
date birth;
date &mybirth = birth;
Both of these, ‘birth’ and ‘mybirth’, would have access to the same member variables and their values. Thus they
can be used interchangeably:
birth.year = 1981;
mybirth.year -= 2;

Can you guess what the value of ‘birth.year’ would be from the above? It would be ‘1979’. The reference
‘mybirth’ is just an alias to ‘birth’ and its group of member variables. Remember that a reference is not a real
variable (it has no value), but simply a nickname for another.
Utilizing pointers with structures, unfortunately, adds a previously unseen complexity. A pointer to a variable
cannot be used to modify the variable’s value until it has been dereferenced. This case is no different from
structures. But it affects the way you access the structure variable’s members. Recall that a pointer simply
contains a memory address. The pointer knows nothing of what this memory address is used for, which is why
you have to dereference a pointer to a specific type. Thus, you cannot access a structure variable’s members
until you dereference a pointer to the structure type:
date birth;
date *p = &birth;
(*p).year = 1979;
The pointer ‘p’ above, had to be dereferenced before the member variable ‘year’ could be accessed through it. It
was surrounded in parenthesis because the indirection operator (asterisk ‘*’) has a lower precedence than the
member operator. Thus the following would not work:
*p.year = 1979;
This would be seen as “get the value pointed to by ‘p.year’” and ‘p.year’ is an invalid identifier; hence the
parenthesis around the indirection operation. This method of accessing a structure variable’s members is
cumbersome and requires two operations simply to get at the member variable: indirection and then member.
For this purpose, there is an operator specifically for accessing a structure variable’s members through a
pointer. This is a member operator known specifically as a pointer-to-member operator or a dash followed by a
greater than sign ‘->’ (also known as an “arrow”):
p->year = 1979;

This operator only works on pointers to structure variables. You cannot use it on normal structure variables for
members. The following would not work:
birth->year = 1979;
The left operand must be a pointer to a variable with members, and the right operand must be the name of a
member variable within that.
7.1 File Management
File handling is an important part of all programs. Most of the applications will have their own features to save
some data to the local disk and read data from the disk again. Files which are on the secondary storage device
are called physical files. In order to process file through program, logical file must be created on the RAM. This
logical file is nothing but an object having file data type. As an object there should be a variable identifier that
points to it. This variable is called file variable and sometimes also called file handler. C++ File I/O classes
simplify such file read/write operations for the programmer by providing easier to use classes.

7.1.1. Streams and Files


The I/O system supplies a consistent interface to the C++ programmer independent of the actual device being
accessed. This provides a level of abstraction between the programmer and the device. This abstraction is called
stream. The actual device is called a file.

6.1.1.1. Streams
The C++ file system is designed to work with a wide variety of devices, including terminals, disk drives, and
tape drives. Even though each device is very different, the C++ file system transforms each into a logical device
called stream. There are two types of streams: text and binary.
a. Text Streams
A text stream is a sequence of characters. In a text stream, certain character translations may occur as required
by the host environment. For example a new line may be converted to a carriage return/linefeed pair. There may
not be a one-to-one relationship between the characters that are written (or read) and those on the external
device. Because of possible transformations, the number of characters written (or read) may not be the same as
those on the external device.
b. Binary streams
A binary stream is a sequence of bytes with a one-to-one correspondence to those in the external device i.e., no
character translations occur. The number of bytes written (or read) is the same as the number on the external
device. However, an implementation-defined number of null bytes may be appended to a binary stream. These
null bytes might be used to pad the information so that it fills a sector on a disk, for example.

6.1.1.2. Files
In C++, a file can be anything from a disk file to a terminal or printer. You associate a stream with a specific file
by performing an open operation. Once a file is open, information can be exchanged between it and a program.
All streams are the same but all files are not. If the file can support position requests, opening that file also
initializes the file position indicator to the start of the file. As each character is read from or written to the file, the
position indicator is incremented. You disassociate a file from a specific stream with a close operation. If you close
a file opened for output, then contents, if any, of its associated stream are written to the external device. -- this
process is referred to as flushing the stream. All files are closed automatically when the program terminates
normally. Files are not closed when a program terminates abnormally. Each stream that is associated with a file
has a file control structure of type FILE. This structure FILE is defined in the header stdio.h.
The File Pointer
A file pointer is a pointer to information that defines various things about the file, including its name, status,
and the current t position of the file. In essence, the file pointer identifies a specific disk file and is used by the
associated stream to direct the operation of the I/O functions. A file pointer is a pointer variable of type FILE.
FILE * fp;

6.1.1.3. The standard streams


When ever a program starts execution, three streams are opened automatically.
stdin --- standard input.
stdout -- standard output
stderr -- standard error
Normally, these streams refer to the console. Because the standard streams are file pointers, they can be used by
the ANSI C file system to perform I/O operations on the console.

6.1.1.4. C++ File I/O Classes and Functions


To perform file I/O, the header file fstream.h is requied. fstream.h defines several classes, including ifstream,
ofstream, and fstream. These classes are derived form istream and ostream, repectively. istream and ostream are
derived form ios.
Three file I/O classes are used for File Read/Write operations:
a. ifstream - Can be used for File read/input operations
b. ofstream - Can be used for File write/output operations
c. fstream - Can be used for both read/write c++ file I/O operations
These classes are derived directly or indirectly from the classes istream, and ostream. We have already used
objects whose types were these classes: cin is an object of class istream and cout is an object of class ostream.
Therefore, we have already been using classes that are related to our file streams. And in fact, we can use our file
streams the same way we are already used to use cin and cout, with the only difference that we have to
associate these streams with physical files. Let's see an example:

6.1.2. Text and Binary Files


In file processing, files are generally classified into two as
 Text file and
 Binary file
Text file is a file in which its content is treated as a sequence of characters and can be accessed sequentially.
Where as binary file is a file in which its content is treated as record sequence in a binary format. Binary format
refers to the actual format the data is going to be placed and processed in the memory which is directly related
to its data type.
For example, the value int count 321 will be stored in three byte if it is written in text file considering the digit
sequence ‘3’, ‘2’, ‘1’. It will be stored in two byte if it is written in binary file since int requires two byte to store
any of its value. When you open the binary file you will see the character equivalence of the two bytes.
321 in binary equals 0000 0001 0100 0001
The first byte holds the character with ASCII value equals to one and the second byte a character with ASCII
value equals 65 which is ‘A’. Then if you open the binary file you will see these characters in place of 321.
6.1.3. Text File processing
File processing involves the following major steps
1. Declaring file variable identifier
2. Opening the file
3. Processing the file
4. Closing the file when process is completed.

6.1.3.1. Opening and Closing a file


An open file is represented within a program by a stream object and any input or output operation performed
on this stream object will be applied to the physical file associated to it. In C++, you open a file by linking it to a
stream. Before you can open a file, you must first obtain a stream. There are three types of streams: input, output,
and input/output. To create an input stream, you must declare the stream to be of class ifstream. To create an output
stream, you must declare it as class ofstream. Streams that will be performing both input and output operations
must be declared as class fstream.
ifstream in ; //input stream
ofstream out ; // output stream
fstream io ; // input and output
Once you have declared a stream, you associate it with a file by using the method open().
The method open ( ) is a member of each of the three stream classes. Its prototype is:
void open (const char *filename, int mode, int access = filebuf::penprot );

Where:
Filename is the name of the file.
The value of the mode determines how the file is opened. It must be one (or more) of these values:

Mode Description

ios::app Write all output to the end of the file

ios::ate Open a file for output and move to the end of the file (normally used to append data to a
file). Data can be written anywhere in the file.

ios::binary Cause the file to be opened in binary mode.


ios::in Open a file for input
ios::nocreate If the file does not exist, the open operation fails.

ios::noreplace If the file exists, the open operation fails.

ios::out Open a file for output


ios:trunc Discard the file's content if it exists (this is also the default action ios::out)

You can combine two or more of these values by using them together.
ofstream out ;
out.open ( "test", ios::out); // correct statement
ofstream out1 ;
out.open ( " test"); // the default value of mode is ios::out –
// correct statment
To open a stream for input and output, you must specify both the ios::in and the ios::out mode values. (Noe
default value for mode is supplied in this case.)
fstream myStream;
myStream.open ( "test", ios::in | ios::out );
If open ( ) fails, myStrream will be zero
if (myStream){
cout << "Cannot open a file.\n";
// handle error
}
Each one of the open() member functions of the classes ofstream, ifstream and fstream has a default mode that
is used if the file is opened without a second argument:
class default mode parameter
ofstream ios::out
ifstream ios::in
fstream ios::in | ios::out
For ifstream and ofstream classes, ios::in and ios::out are automatically and respectively assumed, even if a mode
that does not include them is passed as second argument to the open() member function. The default value is
only applied if the function is called without specifying any value for the mode parameter. If the function is
called with any value in that parameter the default mode is overridden, not combined.
File streams opened in binary mode perform input and output operations independently of any format
considerations. Non-binary files are known as text files, and some translations may occur due to formatting of
some special characters (like newline and carriage return characters).
Since the first task that is performed on a file stream object is generally to open a file, these three classes include
a constructor that automatically calls the open() member function and has the exact same parameters as this
member. Therefore, we could also have declared the previous myfile object and conducted the same opening
operation in our previous example by writing:

ofstream myfile ("example.bin", ios::out | ios::app | ios::binary);


Combining object construction and stream opening in a single statement. Both forms to open a file are valid and
equivalent.
To check if a file stream was successful opening a file, you can do it by calling to member is_open() with no
arguments. This member function returns a bool value of true in the case that indeed the stream object is
associated with an open file, or false otherwise:
if (myfile.is_open()) { /* ok, proceed with output */ }
ifstream myStream ( "myfile" ); // open file for input
When we are finished with our input and output operations on a file we shall close it so that its resources
become available again. In order to do that we have to call the stream's member function close(). This member
function takes no parameters, and what it does is to flush the associated buffers and close the file:
myfile.close();
Once this member function is called, the stream object can be used to open another file, and the file is available
again to be opened by other processes.
In case that an object is destructed while still associated with an open file, the destructor automatically calls
the member function close(). The close method takes no parameters and returns no value.
6.1.3.2. Reading and writing text files
Simply use the << and >> operators in the same way you do when performing console I/O except that instead of
using cin and cout, you substitute a stream that is linked to a file.
ofstream out ("inventory");
out <<"Radios" << 39.95 << endl;
out << "Toastors" << 19.95 << endl;
out.close ( );
Example: Basic file operations
#include <iostream>
#include <fstream>
using namespace std;
int main () {
ofstream myfile;
myfile.open ("example.txt");
myfile << "Writing this to a file.\n";
myfile.close();
return 0;
}
This code creates a file called example.txt and inserts a sentence into it in the same way we are used to do with
cout, but using the file stream myfile instead.
Example 2: writing on a text file
#include <iostream>
#include <fstream>
using namespace std;

int main () {
ofstream myfile ("example.txt");
if (myfile.is_open())
{
myfile << "This is a line.\n";
myfile << "This is another line.\n";
myfile.close();
}
else cout << "Unable to open file";
return 0;
}
Example 3: reading a text file
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main () {
string line;
ifstream myfile ("example.txt");
if (myfile.is_open())
{
while (! myfile.eof() )
{
getline (myfile,line);
cout << line << endl;
}
myfile.close();
}

else cout << "Unable to open file";

return 0;
}
This last example reads a text file and prints out its content on the screen. Notice how we have used a new
member function, called eof() that returns true in the case that the end of the file has been reached. We have
created a while loop that finishes when indeed myfile.eof() becomes true (i.e., the end of the file has been
reached).
Checking state flags
In addition to eof(), which checks if the end of file has been reached, other member functions exist to check the
state of a stream (all of them return a bool value):
Function Description
bad() Returns true if a reading or writing operation fails. For example in the case that we
try to write to a file that is not open for writing or if the device where we try to
write has no space left.
fail() Returns true in the same cases as bad(), but also in the case that a format error
happens, like when an alphabetical character is extracted when we are trying to
read an integer number.
eof() Returns true if a file open for reading has reached the end.
good() It is the most generic state flag: it returns false in the same cases in which calling any
of the previous functions would return true.
In order to reset the state flags checked by any of these member functions we have just seen we can use the
member function clear(), which takes no parameters.
get and put stream pointers
All I/O streams objects have, at least, one internal stream pointer:
 ifstream, like istream, has a pointer known as the get pointer that points to the element to be read in
the next input operation.
 ofstream, like ostream, has a pointer known as the put pointer that points to the location where the
next element has to be written.
 Finally, fstream, inherits both, the get and the put pointers, from iostream (which is itself derived from
both istream and ostream).
These internal stream pointers that point to the reading or writing locations within a stream can be
manipulated using the following member functions:
tellg() and tellp()
These two member functions have no parameters and return a value of the member type pos_type, which is an
integer data type representing the current position of the get stream pointer (in the case of tellg) or the put
stream pointer (in the case of tellp).
seekg() and seekp()
These functions allow us to change the position of the get and put stream pointers. Both functions are
overloaded with two different prototypes. The first prototype is:

seekg ( position );
seekp ( position );
Using this prototype the stream pointer is changed to the absolute position position (counting from the
beginning of the file). The type for this parameter is the same as the one returned by functions tellg and tellp:
the member type pos_type, which is an integer value.
The other prototype for these functions is:

seekg ( offset, direction );


seekp ( offset, direction );
Using this prototype, the position of the get or put pointer is set to an offset value relative to some specific
point determined by the parameter direction. offset is of the member type off_type, which is also an integer
type. And direction is of type seekdir, which is an enumerated type (enum) that determines the point from
where offset is counted from, and that can take any of the following values:
ios::beg offset counted from the beginning of the stream
ios::cur offset counted from the current position of the stream pointer
ios::end offset counted from the end of the stream
The following example uses the member functions we have just seen to obtain the size of a file:
Example: obtaining file size
#include <iostream>
#include <fstream>
using namespace std;
int main () {
long begin,end;
ifstream myfile ("example.txt");
begin = myfile.tellg();
myfile.seekg (0, ios::end);
end = myfile.tellg();
myfile.close();
cout << "size is: " << (end-begin) << " bytes.\n";
return 0;
}

6.1.4. Binary File processing


In binary files, to input and output data with the extraction and insertion operators (<< and >>) and functions
like getline is not efficient, since we do not need to format any data, and data may not use the separation codes
used by text files to separate elements (like space, newline, etc...).
File streams include two member functions specifically designed to input and output binary data sequentially:
write and read. The first one (write) is a member function of ostream inherited by ofstream. And read is a
member function of istream that is inherited by ifstream. Objects of class fstream have both members. Their
prototypes are:

write ( memory_block, size );


read ( memory_block, size );
Where memory_block is of type "pointer to char" (char*), and represents the address of an array of bytes where
the read data elements are stored or from where the data elements to be written are taken. The size parameter is
an integer value that specifies the number of characters to be read or written from/to the memory block.
There are two ways to write and read binary data to and from a file.
 get ( ) and put ( )
 read ( ) and write ( )
If you will be performing binary operations on a file, be sure to open it using the ios::binary mode specifier.

6.1.4.1. get ( ) and put ( )


These functions are byte-oriented.
o get ( ) will read a byte of data.
o put ( ) will write a bye of data.
The get ( ) method has many forms.
istream & get( char & ch );
ostream & put ( char ch);
The get ( ) method read a single character from the associated stream and puts the value in ch, and returns a
reference to the stream. The put ( ) method writes ch to the stream and returns a reference to the stream.

char in;
ifstream in ( "test", ios::in | ios::binary);
if (!in){
cout <<"Cannot open file";
return 1;
}
while (in) //inn will be 0 when eof is reached
{ in.get ( ch );
cout << ch;
}
When the end-of-file is reached, the stream associated with the file becomes zero.
ofstream out ( "chars", io::out | ios::binary);
for (int i= 0; i < 256; i++)
out.put ( (char ) i ) ; //write all characters to disk
out.close ( );

6.1.4.2. read ( ) and write ( )


The read ( ) method reads num bytes from the associated stream, and puts them in a memory buffer (pointed to
by buf).

istream & read ( unsigned char * buf, int num );


The write ( ) method writes num bytes to the associated stream from the memory buffer (pointed to by buf).

ostream & write ( const unsigned char * buf, int num );


If the end-of-file is reached before num characters have been read, then read ( ) simply stops, and the buffer
contains as many characters as were available. You can find out how many characters have been read by using
another member function, called gcount ( ), which has the prototype:

int gcount ( );

6.1.4.3. More get ( ) functions


The method get ( ) is overloaded in several ways.
istream &get (char *buf, int num, char delim = '\n');

This get ( ) method reads characters into the array pointed to by the buf until either num characters have been
read, or the character specified by delim has been encountered. The array pointed to by buf will be null
terminated by get ( ). If the delimiter character is encountered in the input stream, it is not extracted. Instead, it
remains in the stream until the next input operation.

a. int get ( )
It returns the next character from the stream. It returns EOF if the end of file is encountered.
b. getline ( )
istream & getline ( char *buf, int num, char delim ='\n');
This method is virtually identical to the get ( buf, num, delim) version of get ( ). The difference is getline ( ) reads
and removes the delimiter from the input stream.

Example: reading a complete binary file

#include <iostream>
#include <fstream>
using namespace std;

ifstream::pos_type size;
char * memblock;

int main () {
ifstream file ("example.txt", ios::in|ios::binary|ios::ate);
if (file.is_open())
{
size = file.tellg();
memblock = new char [size];
file.seekg (0, ios::beg);
file.read (memblock, size);
file.close();

cout << "the complete file content is in memory";

delete[] memblock;
}
else cout << "Unable to open file";
return 0;
}
In this example the entire file is read and stored in a memory block. Let's examine how this is done:

First, the file is open with the ios::ate flag, which means that the get pointer will be positioned at the end of the
file. This way, when we call to member tellg(), we will directly obtain the size of the file. Notice the type we
have used to declare variable size:

ifstream::pos_type size;
ifstream::pos_type is a specific type used for buffer and file positioning and is the type returned by file.tellg().
This type is defined as an integer type, therefore we can conduct on it the same operations we conduct on any
other integer value, and can safely be converted to another integer type large enough to contain the size of the
file. For a file with a size under 2GB we could use int:

int size;
size = (int) file.tellg();
Once we have obtained the size of the file, we request the allocation of a memory block large enough to hold the
entire file:

memblock = new char[size];


Right after that, we proceed to set the get pointer at the beginning of the file (remember that we opened the file
with this pointer at the end), then read the entire file, and finally close it:

file.seekg (0, ios::beg);


file.read (memblock, size);
file.close();
At this point we could operate with the data obtained from the file. Our program simply announces that the
content of the file is in memory and then terminates.

Function Description
Detecting EOF It returns nonzero when the end of the file has been reached;
int eof ( ); otherwise it returns zero.
Reading and discarding characters from Reads and discards characters until either num characters have been
the input stream. nignored (1 by default ) or until the charcter specified by delim is
istream & ignore (int num = 1, int delim encounterdd (EOF by default). If the delimiting character is
= EOF); encountered, it is not removed from the input stream.
Obtain the next character in the input One can obtain the next character in the input stream without
stream without removing it from that removing it from that stream by using peek ( ). It returns the next
stream character in the stream or EOF if the end of file is encountered.
int peek ( );
One can return the last character read from a stream to that stream
istream & putback ( char c); using putback ( ).

Forcing data to be physically written to When the output is performed, data is not necessarily immediately
the disk written to the physical device linked to the stream. Instead,
information is stored in an internal buffer until the buffer is full.
ostream & flush ( ); Only then are the contents of that buffer written to disk. However,
you can force the information to be physically written to the disk
before the buffer is full by calling flush ( ).

6.1.5. Random Access Files


In C++'s I/O system, you perform random access by using seekg ( ) and seekp ( ) methods.

istream *seekg (streamoff offset, sek_dir origin);


ostream & seekp ( streamoff offet, seek_dir origin);
Here, streamoff is a type defined in iostream.h that is capable of containing the largest valid value that offset can
have. Also seed-dir is an enumeration that has these values:
ios::beg
ios::cur
ios::end
The C++ I/O system manages two pointers associated with a file. One is the get pointer, which specifies where in
then file the next input operation will occur. The other is the put pointer, which specifies where in the file the
next output operation will occur. Each time an input or output operation takes place the appropriate pointer is
automatically sequentially advanced. The seekg ( ) method moves the associated file's current get pointer offset
number of bytes from the specified origin, which must be one of three values. The seekp ( ) method moves the
associated file's current put pointer offset number of bytes from the specified origin, which must be one of three
values.

6.1.5.1. Obtaining the Current File Position


You can determine the current position of each file pointer by using these methods.

streampos tellg ( );
streampows tellp ( );
Here, streampos is a type defined in iostream.h that is capable of holding the largest value that either function
can return.

6.1.5.2. I/O Status


The C++ I/O system maintains status information about the outcome of each I/O operation. The current state of
the I/O system is held in an integer, in which the following flags are encoded.

 eofbit -- 1 when end-of-file is encountered; 0 otherwise


 failbit -- 1 when a (possibly) nonfatal I/O error has occurred; 0 otherwise
 badbit -- 1 when a fatal I/O error has ocurred; 0 otherwise

These flags are enumerated inside ios. Also defined in ios is goodbit, which has the value 0. There are two ways
in which you can obtain I/O status information.

a. Use the rdstate function/method.

int rdstate ( );
rdstate function returns the current status of the error flags encoded into an integer. It returns zero, when no
error has occurred. Otherwise, an error bit is turned on.

b. Use one or more of these methods.

Method Description
int bad ( ) Returns true if badbit is set.
int fail ( ) Returns true if failbit is set.
int eof ( ) Returns true if there are no errors.
int good ( ) Otherwise they return false.

Once an error has occurred, it may need to be cleared before your program continues. to do this, use the clear ( )
method.
void clear ( int flags = 0);
If flag = zero (as it is by default), all error flags are cleared (reset to zero). Otherwise, set flags to the flags or
values you want to clear.

6.1.1. File Streams as Function Arguments


A file stream object can be a function argument. The only requirement is that the function’s formal parameter
be a reference to an appropriate stream, either as ifstream& or ofstream&. See the following example
#include <fstream.h>
#include<stdlib.h>
void in-out(ofstream&);
int main()
{
const int MAXCHARS = 21;
char fname[MAXCHARS]=”list.dat”;
ofstream out-file(fame);

if(out-file.fail())
{
cout<<”file open error”;
exit(1);
}
in-out(out-file);
return 0;
}
void in-out(ofstream& file-out)
{
const int LINELEN= 80;
const int NUMLINES = 5;
int count;
char line[LINELEN] ;
cout<<”please enter five lines of text”<<endl;
for(cout=0;cout<NUMLINES;count++)
{
file-out<<line<<endl;
}
return;
}

6.1.2. Buffers and Synchronization


When we operate with file streams, these are associated to an internal buffer of type streambuf. This buffer is a
memory block that acts as an intermediary between the stream and the physical file. For example, with an
ofstream, each time the member function put (which writes a single character) is called, the character is not
written directly to the physical file with which the stream is associated. Instead of that, the character is
inserted in that stream's intermediate buffer.
When the buffer is flushed, all the data contained in it is written to the physical medium (if it is an output
stream) or simply freed (if it is an input stream). This process is called synchronization and takes place under
any of the following circumstances:

 When the file is closed: before closing a file all buffers that have not yet been flushed are
synchronized and all pending data is written or read to the physical medium.

 When the buffer is full: Buffers have a certain size. When the buffer is full it is automatically
synchronized.

 Explicitly, with manipulators: When certain manipulators are used on streams, an explicit
synchronization takes place. These manipulators are: flush and endl.

 Explicitly, with member function sync(): Calling stream's member function sync(), which takes
no parameters, causes an immediate synchronization. This function returns an int value equal to -1
if the stream has no associated buffer or in case of failure. Otherwise (if the stream buffer was
successfully synchronized) it returns 0.

Exercise

1. Write a program that accept N student record from the keyboard & store the list on a file “D:\\ Test.txt” in
a text file format
2. Write a program that reads students record from the text file “D:|\ Test.txt” and display on the screen.
3. Do Q1 in binary format.
4. Do Q2 in binary format.
Note Student record consists of first name, last name, gender, age and Id.
Solution
Consider the file Header .h which contains the basic preprocessing include files, structure definition and
functions other than the main function.
Header. h // this file is saved at D: and it has the following structure.
#include<iostream.h>
#include<fstream.h>
#include<conio.h>
struct studList
{
char firstName[12];
char lastName[12];
int age;
char gender;
char Id[12];
};
studList getStudent()
{
studList std;
cout<<"Enter student first name ===>";cin>>std.firstName;
cout<<"Enter student last name ===>";cin>>std.lastName;
cout<<"Enter student age ===>";cin>>std.age;
cout<<"Enter student gender ===>";cin>>std.gender;
cout<<"Enter student Id ===>";cin>>std.Id;
return std;
}
void DisplayStudent(studList std)
{
cout<<"Student first name:\t"<<std.firstName<<endl;
cout<<"Student last name :\t"<<std.lastName<<endl;
cout<<"Student age:\t\t"<<std.age<<endl;
cout<<"Student gender:\t\t"<<std.gender<<endl;
cout<<"Student Id:\t\t"<<std.Id<<endl;
}
Solution 1
#include "D:\ header.h"
int main() {
studList std;
fstream outf;
outf.open("d:\\test.txt",ios::app);
if(outf.fail()) {
cout<<"unable to open the file d:\test.txt\n";
return 1;
}
clrscr();
int N;
cout<<"Enter the number of students ===> ";cin>>N;
for(int i = 0; i < N; i++) {
std = getStudent();
clrscr();
outf<<std.firstName<<" "<<std.lastName<<" "<<std.Id<<" " <<std.gender<<" "<<std.age<<endl;
}
getch();
return 0;
}

Solution 2
#include "D:\ header.h"
int main(){
studList std;
fstream inpf;
inpf.open("d:\\test.txt",ios::in);
if(inpf.fail())
{
cout<<"unable to open the file d:\test.txt\n"; return 1;
}
clrscr();
while (!inpf.eof())
{
inpf>>std.firstName>>std.lastName>>std.Id>>std.gender>>std.age;
if(inpf.eof()) break;
DisplayStudent(std);
cout<<"=====================================\n";
getch();
}
inpf.close();
return 0;
}
Solution 3
#include "D:\ header.h"
int main()
{
studList std;
fstream outf;
outf.open("d:\\test2.txt",ios::app|ios::binary);
if(outf.fail())
{
cout<<"unable to open the file d:\test.txt\n"; return 1;
}
clrscr();
int N;
cout<<"Enter the number of students ===> ";cin>>N;
for(int i = 0; i < N; i++)
{
std = getStudent();
clrscr();
outf.write((char *) &std, sizeof(std));
}
getch();
return 0;
}
Solution 4
#include "D:\ header.h"
int main() {
studList std;
fstream inpf;
inpf.open("d:\\test2.txt",ios::in|ios::binary);
if(inpf.fail())
{
cout<<"unable to open the file d:\test.txt\n";
return 1;
}
clrscr();
while (!inpf.eof()){
{
inpf.read((char*)&std, sizeof(std));
//inpf>>std.firstName>>std.lastName>>std.Id>>std.gender>>std.age;
if(inpf.eof()) break;
DisplayStudent(std);
cout<<"======================================\n";
getch();
}
inpf.close();
return 0;
} Annex

6.1.3. Prototypes of C++ file I/O methods


Void open ( const char * filename, int mode, int access = filebuf::openprot ) ;

File open modes:


ios::app Write all output to the end of the file
ios::ate Open a file for output and move to the end of the file (normally used to append
data to a file). Data can be written anywhere in the file.

ios::binary Cause the to be opened in binary mode.


ios::in Open a file for input
ios::out Open a file for output
ios::trunc Discard the file's content if it exists (this is also the default action ios::out)
ios::nocreate If the file does not exist, the open operation fails.
ios::noreplace If the file exists, the open operation fails.
istream & get ( char & ch ) ;
ostream & put ( char ch ) ;
istream & read ( unsigned char * buf, int num ) ;
ostream & write ( const unsigned char * buf, int num ) ;
int gcount ( ) ;
istream & get ( char * buf, int num, char delim = '\n' ) ;
int get ( ) ;
istream & getline ( char * buf, int num, char delim = '\n' ) ;
int eof ( );
istream & ignore ( int num = 1, int delim = EOF ) ;
int peek ( ) ;
istream & putback ( char c ) ;
ostream & flush ( ) ;
istream & seekg ( streamoff offset, seek_dir origin ) ;
ostream & seekp (streamoff offset, seek_dir origin ) ;
origin must be one of three values:
ios::beg -- Beginning of file
ios::cur -- Current location
ios::end -- End of file

streampos tellg ( ) ;
stream pos tellp ( ) ;
istream & seekg ( streampos pos ) ;
ostream * seekp (streampos pos ) ;
int rdstate ( ) ;
inr bad ( ) ;
int eof ( ) ;
int fail ( ) ;
int good ( );
void clear ( int flags = 0 ) ;
Chapter Five
File Management
4.1. File Management
File handling is an important part of all programs. Most of the applications will have their own features to save
some data to the local disk and read data from the disk again. Files which are on the secondary storage device are
called physical files. In order to process file through program, logical file must be created on the RAM. This logical
file is nothing but an object having file data type. As an object there should be a variable identifier that points to it.
This variable is called file variable and some times also called file handler. C++ File I/O classes simplify such file
read/write operations for the programmer by providing easier to use classes.
4.1.1. Streams and Files
The I/O system supplies a consistent interface to the C++ programmer independent of the actual device being
accessed. This provides a level of abstraction between the programmer and the device. This abstraction is called
stream. The actual device is called a file.
4.1.1.1. Streams
The C++ file system is designed to work with a wide variety of devices, including terminals, disk drives, and tape
drives. Even though each device is very different, the C++ file system transforms each into a logical device called
stream. There are two types of streams: text and binary.
a. Text Streams
A text stream is a sequence of characters. In a text stream, certain character translations may occur as required by
the host environment. For example a new line may be converted to a carriage return/linefeed pair. There may not be
a one-to-one relationship between the characters that are written (or read) and those on the external device.
Because of possible transformations, the number of characters written (or read) may not be the same as those on the
external device.
b. Binary streams
A binary stream is a sequence of bytes with a one-to-one correspondence to those in the external device i.e., no
character translations occur. The number of bytes written (or read) is the same as the number on the external
device. However, an implementation-defined number of null bytes may be appended to a binary stream. These null
bytes might be used to pad the information so that it fills a sector on a disk, for example.
4.1.1.2. Files
In C++, a file can be anything from a disk file to a terminal or printer. You associate a stream with a specific file by
performing an open operation. Once a file is open, information can be exchanged between it and a program. All
streams are the same but all files are not. If the file can support position requests, opening that file also initializes the
file position indicator to the start of the file. As each character is read from or written to the file, the position indicator is
incremented. You disassociate a file from a specific stream with a close operation. If you close a file opened for output,
then contents, if any, of its associated stream are written to the external device. -- this process is referred to as
flushing the stream. All files are closed automatically when the program terminates normally. Files are not closed

1
when a program terminates abnormally. Each stream that is associated with a file has a file control structure of type
FILE. This structure FILE is defined in the header stdio.h.
The File Pointer
A file pointer is a pointer to information that defines various things about the file, including its name, status, and
the current t position of the file. In essence, the file pointer identifies a specific disk file and is used by the
associated stream to direct the operation of the I/O functions. A file pointer is a pointer variable of type FILE.
FILE * fp;
4.1.1.3. The standard streams
When ever a program starts execution, three streams are opened automatically.
stdin --- standard input.
stdout -- standard output
stderr -- standard error
Normally, these streams refer to the console. Because the standard streams are file pointers, they can be used by the
ANSI C file system to perform I/O operations on the console.
4.1.1.4. C++ File I/O Classes and Functions
To perform file I/O, the header file fstream.h is requied. fstream.h defines several classes, including ifstream, ofstream,
and fstream. These classes are derived form istream and ostream, repectively. istream and ostream are derived form ios.
Three file I/O classes are used for File Read/Write operations:
a. ifstream - Can be used for File read/input operations
b. ofstream - Can be used for File write/output operations
c. fstream - Can be used for both read/write c++ file I/O operations
These classes are derived directly or indirectly from the classes istream, and ostream. We have already used objects
whose types were these classes: cin is an object of class istream and cout is an object of class ostream. Therefore, we
have already been using classes that are related to our file streams. And in fact, we can use our file streams the same
way we are already used to use cin and cout, with the only difference that we have to associate these streams with
physical files. Let's see an example:
4.1.2. Text and Binary Files
In file processing, files are generally classified into two as
 Text file and
 Binary file
Text file is a file in which its content is treated as a sequence of characters and can be accessed sequentially. Where
as binary file is a file in which its content is treated as record sequence in a binary format. Binary format refers to the
actual format the data is going to be placed and processed in the memory which is directly related to its data type.
For example, the value int count 321 will be stored in three byte if it is written in text file considering the digit
sequence ‘3’, ‘2’, ‘1’. It will be stored in two byte if it is written in binary file since int requires two byte to store any
of its value. When you open the binary file you will see the character equivalence of the two bytes.
321 in binary equals 0000 0001 0100 0001

2
The first byte holds the character with ASCII value equals to one and the second byte a character with ASCII value
equals 65 which is ‘A’. Then if you open the binary file you will see these characters in place of 321.
4.1.3. Text File processing
File processing involves the following major steps
1. Declaring file variable identifier
2. Opening the file
3. Processing the file
4. Closing the file when process is completed.
4.1.3.1. Opening and Closing a file
An open file is represented within a program by a stream object and any input or output operation performed on
this stream object will be applied to the physical file associated to it. In C++, you open a file by linking it to a
stream. Before you can open a file, you must first obtain a stream. There are three types of streams: input, output, and
input/output. To create an input stream, you must declare the stream to be of class ifstream. To create an output stream,
you must declare it as class ofstream. Streams that will be performing both input and output operations must be
declared as class fstream.
ifstream in ; //input stream
ofstream out ; // output stream
fstream io ; // input and output
Once you have declared a stream, you associate it with a file by using the method open().
The method open ( ) is a member of each of the three stream classes. Its prototype is:
void open (const char *filename, int mode, int access = filebuf::penprot );
Where:
Filename is the name of the file.
The value of the mode determines how the file is opened. It must be one (or more) of these values:
Mode Description
ios::app Write all output to the end of the file
ios::ate Open a file for output and move to the end of the file (normally used to append data to a
file). Data can be written anywhere in the file.
ios::binary Cause the file to be opened in binary mode.
ios::in Open a file for input
ios::nocreate If the file does not exist, the open operation fails.
ios::noreplace If the file exists, the open operation fails.
ios::out Open a file for output
ios:trunc Discard the file's content if it exists (this is also the default action ios::out)
You can combine two or more of these values by using them together.
ofstream out ;
out.open ( "test", ios::out); // correct statement

3
ofstream out1 ;
out.open ( " test"); // the default value of mode is ios::out – // correct statment
To open a stream for input and output, you must specify both the ios::in and the ios::out mode values. (Noe default
value for mode is supplied in this case.)
fstream myStream;
myStream.open ( "test", ios::in | ios::out );
If open ( ) fails, myStrream will be zero
if (myStream){
cout << "Cannot open a file.\n";
// handle error
}
Each one of the open() member functions of the classes ofstream, ifstream and fstream has a default mode that is
used if the file is opened without a second argument:
class default mode parameter
ofstream ios::out
ifstream ios::in
fstream ios::in | ios::out
For ifstream and ofstream classes, ios::in and ios::out are automatically and respectively assumed, even if a mode
that does not include them is passed as second argument to the open() member function. The default value is only
applied if the function is called without specifying any value for the mode parameter. If the function is called with
any value in that parameter the default mode is overridden, not combined.
File streams opened in binary mode perform input and output operations independently of any format
considerations. Non-binary files are known as text files, and some translations may occur due to formatting of some
special characters (like newline and carriage return characters).
Since the first task that is performed on a file stream object is generally to open a file, these three classes include a
constructor that automatically calls the open() member function and has the exact same parameters as this
member. Therefore, we could also have declared the previous myfile object and conducted the same opening
operation in our previous example by writing:

ofstream myfile ("example.bin", ios::out | ios::app | ios::binary);

Combining object construction and stream opening in a single statement. Both forms to open a file are valid and
equivalent.
To check if a file stream was successful opening a file, you can do it by calling to member is_open() with no
arguments. This member function returns a bool value of true in the case that indeed the stream object is associated
with an open file, or false otherwise:
if (myfile.is_open()) { /* ok, proceed with output */ }
ifstream myStream ( "myfile" ); // open file for input

4
When we are finished with our input and output operations on a file we shall close it so that its resources become
available again. In order to do that we have to call the stream's member function close(). This member function
takes no parameters, and what it does is to flush the associated buffers and close the file:
myfile.close();
Once this member function is called, the stream object can be used to open another file, and the file is available
again to be opened by other processes.
In case that an object is destructed while still associated with an open file, the destructor automatically calls the
member function close(). The close method takes no parameters and returns no value.
4.1.3.2. Reading and writing text files
Simply use the << and >> operators in the same way you do when performing console I/O except that instead of using
cin and cout, you substitute a stream that is linked to a file.
ofstream out ("inventory");
out <<"Radios" << 39.95 << endl;
out << "Toastors" << 19.95 << endl;
out.close ( );
Example: Basic file operations
#include <iostream>
#include <fstream>
using namespace std;
int main () {
ofstream myfile;
myfile.open ("example.txt");
myfile << "Writing this to a file.\n";
myfile.close();
return 0;
}
This code creates a file called example.txt and inserts a sentence into it in the same way we are used to do with
cout, but using the file stream myfile instead.
Example 2: writing on a text file
#include <iostream>
#include <fstream>
using namespace std;
int main () {
ofstream myfile ("example.txt");
if (myfile.is_open())
{
myfile << "This is a line.\n";
myfile << "This is another line.\n";
myfile.close();
}

5
else cout << "Unable to open file";
return 0;
}
Example 3: reading a text file
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main () {
string line;
ifstream myfile ("example.txt");
if (myfile.is_open())
{
while (! myfile.eof() )
{
getline (myfile,line);
cout << line << endl;
}
myfile.close();
}

else cout << "Unable to open file";


return 0;
}
This last example reads a text file and prints out its content on the screen. Notice how we have used a new member
function, called eof() that returns true in the case that the end of the file has been reached. We have created a while
loop that finishes when indeed myfile.eof() becomes true (i.e., the end of the file has been reached).
Checking state flags
In addition to eof(), which checks if the end of file has been reached, other member functions exist to check the
state of a stream (all of them return a bool value):
Function Description
bad() Returns true if a reading or writing operation fails. For example in the case that we
try to write to a file that is not open for writing or if the device where we try to
write has no space left.
fail() Returns true in the same cases as bad(), but also in the case that a format error
happens, like when an alphabetical character is extracted when we are trying to
read an integer number.
eof() Returns true if a file open for reading has reached the end.
good() It is the most generic state flag: it returns false in the same cases in which calling any
of the previous functions would return true.
In order to reset the state flags checked by any of these member functions we have just seen we can use the member
function clear(), which takes no parameters.

6
get and put stream pointers
All I/O streams objects have, at least, one internal stream pointer:
 ifstream, like istream, has a pointer known as the get pointer that points to the element to be read in the
next input operation.
 ofstream, like ostream, has a pointer known as the put pointer that points to the location where the next
element has to be written.
 Finally, fstream, inherits both, the get and the put pointers, from iostream (which is itself derived from
both istream and ostream).
These internal stream pointers that point to the reading or writing locations within a stream can be manipulated
using the following member functions:
tellg() and tellp()
These two member functions have no parameters and return a value of the member type pos_type, which is an
integer data type representing the current position of the get stream pointer (in the case of tellg) or the put stream
pointer (in the case of tellp).
seekg() and seekp()
These functions allow us to change the position of the get and put stream pointers. Both functions are overloaded
with two different prototypes. The first prototype is:
seekg ( position );
seekp ( position );
Using this prototype the stream pointer is changed to the absolute position position (counting from the beginning
of the file). The type for this parameter is the same as the one returned by functions tellg and tellp: the member type
pos_type, which is an integer value.
The other prototype for these functions is:
seekg ( offset, direction );
seekp ( offset, direction );
Using this prototype, the position of the get or put pointer is set to an offset value relative to some specific point
determined by the parameter direction. offset is of the member type off_type, which is also an integer type. And
direction is of type seekdir, which is an enumerated type (enum) that determines the point from where offset is
counted from, and that can take any of the following values:
ios::beg offset counted from the beginning of the stream
ios::cur offset counted from the current position of the stream pointer
ios::end offset counted from the end of the stream
The following example uses the member functions we have just seen to obtain the size of a file:
Example: obtaining file size
#include <iostream>
#include <fstream>
using namespace std;
int main () {

7
long begin,end;
ifstream myfile ("example.txt");
begin = myfile.tellg();
myfile.seekg (0, ios::end);
end = myfile.tellg();
myfile.close();
cout << "size is: " << (end-begin) << " bytes.\n";
return 0;
}
4.1.4. Binary File processing
In binary files, to input and output data with the extraction and insertion operators (<< and >>) and functions like
getline is not efficient, since we do not need to format any data, and data may not use the separation codes used by
text files to separate elements (like space, newline, etc...).
File streams include two member functions specifically designed to input and output binary data sequentially:
write and read. The first one (write) is a member function of ostream inherited by ofstream. And read is a member
function of istream that is inherited by ifstream. Objects of class fstream have both members. Their prototypes are:
write ( memory_block, size );
read ( memory_block, size );
Where memory_block is of type "pointer to char" (char*), and represents the address of an array of bytes where the
read data elements are stored or from where the data elements to be written are taken. The size parameter is an
integer value that specifies the number of characters to be read or written from/to the memory block.
There are two ways to write and read binary data to and from a file.
 get ( ) and put ( )
 read ( ) and write ( )
If you will be performing binary operations on a file, be sure to open it using the ios::binary mode specifier.
4.1.4.1. get ( ) and put ( )
These functions are byte-oriented.
o get ( ) will read a byte of data.
o put ( ) will write a bye of data.
The get ( ) method has many forms.
istream & get( char ch );
ostream & put ( char ch);
The get ( ) method read a single character from the associated stream and puts the value in ch, and returns a
reference to the stream. The put ( ) method writes ch to the stream and returns a reference to the stream.

char in;
ifstream in ( "test", ios::in | ios::binary);
if (!in){

8
cout <<"Cannot open file";
return 1;
}
while (in) //in will be 0 when eof is reached
{ in.get ( ch );
cout << ch;
}
When the end-of-file is reached, the stream associated with the file becomes zero.
ofstream out ( "chars", io::out | ios::binary);
for (int i= 0; i < 256; i++)
out.put ( (char ) i ) ; //write all characters to disk
out.close ( );
4.1.4.2. read ( ) and write ( )
The read ( ) method reads num bytes from the associated stream, and puts them in a memory buffer (pointed to by
buf).
istream & read ( unsigned char * buf, int num );
The write ( ) method writes num bytes to the associated stream from the memory buffer (pointed to by buf).
ostream & write ( const unsigned char * buf, int num );
If the end-of-file is reached before num characters have been read, then read ( ) simply stops, and the buffer contains
as many characters as were available. You can find out how many characters have been read by using another
member function, called gcount ( ), which has the prototype:
int gcount ( );
4.1.4.3. More get ( ) functions
The method get ( ) is overloaded in several ways.
istream &get (char *buf, int num, char delim = '\n');
This get ( ) method reads characters into the array pointed to by the buf until either num characters have been read,
or the character specified by delim has been encountered. The array pointed to by buf will be null terminated by get
( ). If the delimiter character is encountered in the input stream, it is not extracted. Instead, it remains in the stream
until the next input operation.

a. int get ( )
It returns the next character from the stream. It returns EOF if the end of file is encountered.
b. getline ( )
istream & getline ( char *buf, int num, char delim ='\n');
This method is virtually identical to the get ( buf, num, delim) version of get ( ). The difference is getline ( ) reads and
removes the delimiter from the input stream.
Example: reading a complete binary file
#include <iostream.h>

9
#include <fstream.h>
using namespace std;
char * memblock;
int main () {
ifstream file ("example.txt", ios::in|ios::binary|ios::ate);
if (file.is_open())
{
long size = file.tellg();
memblock = new char [size];
file.seekg (0, ios::beg);
file.read (memblock, size);
file.close();
cout << "the complete file content is in memory";
delete[] memblock;
}
else cout << "Unable to open file";
return 0;
}
In this example the entire file is read and stored in a memory block. Let's examine how this is done:
First, the file is open with the ios::ate flag, which means that the get pointer will be positioned at the end of the file.
This way, when we call to member tellg(), we will directly obtain the size of the file. Once we have obtained the
size of the file, we request the allocation of a memory block large enough to hold the entire file:
memblock = new char[size];
Right after that, we proceed to set the get pointer at the beginning of the file (remember that we opened the file
with this pointer at the end), then read the entire file, and finally close it:
file.seekg (0, ios::beg);
file.read (memblock, size);
file.close();
At this point we could operate with the data obtained from the file. Our program simply announces that the
content of the file is in memory and then terminates.

Function Description
Detecting EOF It returns nonzero when the end of the file has been reached;
int eof ( ); otherwise it returns zero.
Reading and discarding characters from Reads and discards characters until either num characters have been
the input stream. nignored (1 by default ) or until the charcter specified by delim is
istream & ignore (int num = 1, int delim encounterdd (EOF by default). If the delimiting character is
= EOF); encountered, it is not removed from the input stream.
Obtain the next character in the input One can obtain the next character in the input stream without
stream without removing it from that removing it from that stream by using peek ( ). It returns the next
stream character in the stream or EOF if the end of file is encountered.

10
int peek ( );
One can return the last character read from a stream to that stream
istream & putback ( char c); using putback ( ).

Forcing data to be physically written to When the output is performed, data is not necessarily immediately
the disk written to the physical device linked to the stream. Instead,
information is stored in an internal buffer until the buffer is full.
ostream & flush ( ); Only then are the contents of that buffer written to disk. However,
you can force the information to be physically written to the disk
before the buffer is full by calling flush ( ).

4.1.5. Random Access Files


In C++'s I/O system, you perform random access by using seekg ( ) and seekp ( ) methods.
istream *seekg (streamoff offset, sek_dir origin);
ostream & seekp ( streamoff offet, seek_dir origin);
Here, streamoff is a type defined in iostream.h that is capable of containing the largest valid value that offset can
have. Also seed-dir is an enumeration that has these values:
ios::beg
ios::cur
ios::end
The C++ I/O system manages two pointers associated with a file. One is the get pointer, which specifies where in then
file the next input operation will occur. The other is the put pointer, which specifies where in the file the next
output operation will occur. Each time an input or output operation takes place the appropriate pointer is
automatically sequentially advanced. The seekg ( ) method moves the associated file's current get pointer offset
number of bytes from the specified origin, which must be one of three values. The seekp ( ) method moves the
associated file's current put pointer offset number of bytes from the specified origin, which must be one of three
values.
4.1.5.1. Obtaining the Current File Position
You can determine the current position of each file pointer by using these methods.
streampos tellg ( );
streampows tellp ( );
Here, streampos is a type defined in iostream.h that is capable of holding the largest value that either function can
return.
4.1.5.2. I/O Status
The C++ I/O system maintains status information about the outcome of each I/O operation. The current state of the
I/O system is held in an integer, in which the following flags are encoded.
 eofbit -- 1 when end-of-file is encountered; 0 otherwise

11
 failbit -- 1 when a (possibly) nonfatal I/O error has occurred; 0 otherwise
 badbit -- 1 when a fatal I/O error has ocurred; 0 otherwise
These flags are enumerated inside ios. Also defined in ios is goodbit, which has the value 0. There are two ways in
which you can obtain I/O status information.
a. Use the rdstate function/method.
int rdstate ( );
rdstate function returns the current status of the error flags encoded into an integer. It returns zero, when no error
has occurred. Otherwise, an error bit is turned on.
b. Use one or more of these methods.
Method Description
int bad ( ) Returns true if badbit is set.
int fail ( ) Returns true if failbit is set.
int eof ( ) Returns true if there are no errors.
int good ( ) Otherwise they return false.
Once an error has occurred, it may need to be cleared before your program continues. to do this, use the clear ( )
method.
void clear ( int flags = 0);
If flag = zero (as it is by default), all error flags are cleared (reset to zero). Otherwise, set flags to the flags or value s
you want to clear.
Exercise
1. Write a program that accept N student record from the keyboard & store the list on a file “D:\\ Test.txt” in a
text file format
2. Write a program that reads students record from the text file “D:|\ Test.txt” and display on the screen.
3. Do Q1 in binary format.
4. Do Q2 in binary format.
Note Student record consists of first name, last name, gender, age and Id.
4.1.6. File Streams as Function Arguments
A file stream object can be a function argument. The only requirement is that the function’s formal parameter be a
reference to an appropriate stream, either as ifstream& or ofstream&. See the following example
#include <fstream.h>
#include<stdlib.h>
void in-out(ofstream&);
int main()
{
const int MAXCHARS = 21;
char fname[MAXCHARS]=”list.dat”;
ofstream out-file(fame);

12
if(out-file.fail())
{
cout<<”file open error”;
exit(1);
}
in-out(out-file);
return 0;
}
void in-out(ofstream& file-out)
{
const int LINELEN= 80;
const int NUMLINES = 5;
int count;
char line[LINELEN] ;
cout<<”please enter five lines of text”<<endl;
for(cout=0;cout<NUMLINES;count++)
{
file-out<<line<<endl;
}
return;
}

13

You might also like