Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Introduction To Programming

Download as pdf or txt
Download as pdf or txt
You are on page 1of 104

AAU

Introduction to Programming
Instructor Seble
M.T

2005

WWW.AAU.EDU.ET

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: x Data characteristics x 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 Amharic), 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 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.
1

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. 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 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.

Low-level languages

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 languagesso 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 languages are characterized (categorized) is by programming paradigm. A programming paradigm provides the programmer's view
2

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 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 wellknown 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.

Structured Programming Languages

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 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. 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: x The first part focuses on defining the problem and logical procedures to follow in solving it. x 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.
3

1.2 Problem solving Techniques

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 Pseudocode. We will use the three methods here. Generally, flowcharts work well for small problems but Pseudocode is used for larger problems.

1.2.1 Pseudocode
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 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 environmentindependent 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 highlevel in nature. Example: Write a program that asks the user to enter a temperature reading in centigrade and then prints the equivalent Fahrenheit value. Process x Prompt for centigrade value x Read centigrade value x Compute Fahrenheit value x Display Fahrenheit value Output Fahrenheit

Input Centigrade

CelsusToFarh (main func)

centigard InPutCen

centigard CalcFar

Fahrenheit OutF Pa uh tF an r heit re

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 doesnt 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.

Example 1: - Draw flow chart of an algorithm to add two numbers and display their result. Algorithm description x x x x 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

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 too and counter to 1 1.1.If the counter is less than or equal to 50 Add counter to sum 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 is 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. 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
x

How well the proposed system supports the strategic objectives of the organization.
8

b. Economic Feasibility
x x x x

Cost savings Increased revenue Decreased investment Increased profits

c. Technical Feasibility
x

Hardware, software, and network capability, reliability, and availability

d. Operational Feasibility
x x x

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 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
9

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. 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.

1.3.6 Unit testing

10

Chapter Two 2. C++ Basics ........................................................................................ 2


2.1. 2.2. 2.3. 2.4. Structure of C++ Program................................................................................... 2 C++ IDE .............................................................................................................. 2 Showing Sample program ................................................................................... 3 Basic Elements .................................................................................................... 5 Keywords (reserved words) ........................................................................ 5 Identifiers .................................................................................................... 5 Literals ........................................................................................................ 6 Comments ................................................................................................... 6 Variables ..................................................................................................... 7 Basic Data Types ........................................................................................ 9 Signed and Unsigned ................................................................................ 10 Characters ................................................................................................. 12 Characters and Numbers ........................................................................... 12 Assignment Operators ............................................................................... 13 Arithmetic Operators ................................................................................ 14 Relational Operators ................................................................................. 15 Logical Operators...................................................................................... 16 Bitwise Operators...................................................................................... 17 Increment/decrement Operators ................................................................ 18

2.4.1. 2.4.2. 2.4.3. 2.4.4. 2.5. 2.5.1. 2.5.2. 2.5.3. 2.5.4. 2.5.5. 2.6. 2.6.1. 2.6.2. 2.6.3. 2.6.4. 2.6.5. 2.6.6. 2.7. 2.8. 2.9. 2.10.

Data Types, Variables, and Constants ................................................................ 7

Operators ........................................................................................................... 12

Precedence of Operators ................................................................................... 18 Simple Type Conversion................................................................................... 20 Statements ......................................................................................................... 21 Input/Output Statements ............................................................................... 21

-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, plaintext 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>

-2-

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 .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.

-3-

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 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.

-4-

The main() function ends on line 7 with the closing brace.

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 asm const_cast dynamic_cast explicit goto namespace reinterpret_cast static_cast throw union wchar_t auto class do extern if new register static true unsigned are some of the reserved words of C++. bool const double false inline operator return struct tr y us i ng break char delete float int private short switch typedef virtual case continue else for l ong protected signed template typeid void catch default enum friend mutable public sizeof this typename volatile

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 Letter Letter

Syntax of an identifier

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 man y 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 Int _Pressure first_one Although using _Pressure is not recommended. The following are invalid: days-in-year throw 1data my__best int No## first.val bestWish! Profit95 first_1

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.

-6-

Anything enclosed by the pair /* and */ is considered a comment.

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.

-7-

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 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. -8-

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 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.

unsigned short int short int(signed short int) unsigned long int long int(signed long int) int unsigned int signed int char float double long double

Type

2 bytes 2 bytes 4 bytes 4 bytes 2 bytes 2 bytes 2 bytes 1 byte 4 bytes 8 bytes 10 bytes

Size

0 to 65,535 -32,768 to 32,767 0 to 4,294,967,295 -2,147,483,648 to 2,147,483,647 -32,768 to 32,767 0 to 65,535 -32,768 to 32,767 256 character values 3.4e-38 to 3.4e38 1.7e-308 to 1.7e308 1.2e-4932 to 1.2e4932

Values

Table C++ data types and their ranges -9-

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 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.
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: }

Example: A demonstration of the use of variables.

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. 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. - 10 -

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 1: #include <iostream.h> 2: int main() 3: { 4: unsigned short int smallNumber; 5: smallNumber = 65535; 6: cout << "small number:" << smallNumber 7: smallNumber++; 8: cout << "small number:" << smallNumber 9: smallNumber++; 10: cout << "small number:" << smallNumber 11: return 0; 12: } Output: small number:65535 small number:0 small number:1 a value in a variable

<< endl; << endl; << endl;

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.
Example: A demonstration of integer. 1: #include <iostream.h> 2: int main() 3: { 4: short int smallNumber; 5: smallNumber = 32767; 6: cout << "small number:" 7: smallNumber++; 8: cout << "small number:" 9: smallNumber++; 10: cout << "small number:" 11: return 0; 12: } Output: small number:32767 adding too large a number to a signed

<< smallNumber << endl; << smallNumber << endl; << smallNumber << endl;

- 11 -

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. 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

- 12 -

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 r = += -= *= /= %= &= |= ^= <<= >>= Example n = 25 n += 25 n -= 25 n *= 25 n /= 25 n %= 25 n &= 0xF2F2 n |= 0xF2F2 n ^= 0xF2F2 n <<= 4 n >>= 4 Equivalent To n = n + 25 n = n - 25 n = n * 25 n = n / 25 n = n % 25 n = n & 0xF2F2 n = n | 0xF2F2 n = n ^ 0xF2F2 n = 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; m = (n = p = 100) + 2; // means: n = (m = (p = 100)); // means: m = (n = (p = 100)) + 2;

This is equally applicable to other forms of assignment. For example:

- 13 -

m = 100; m += n = p = 10; 2.6.2. Arithmetic Operators

// means: m = m + (n = p = 10);

C++ provides five basic arithmetic operators. These are summarized in table below Operator + * / % Name Example Addition 12 + 4.9 Subtraction 3.98 - 4 Multiplication 2 * 3.4 Division 9 / 2.0 Remainder 13 % 3 Arithmetic operators. // gives 16.9 // gives -0.02 // gives 6.8 // gives 4.5 //gives 1

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). 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 -9 / 2 // gives 4, not 4.5! // 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 int double cost = 100; volume = 80; 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.

- 14 -

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 Parameter Type(s) Result below.

Header File
<stdlib.h> <math.h> <math.h> <math.h> <math.h> <math.h> <math.h>

Function
abs(i) cos(x) fabs(x) pow(x, y) sin(x) sqrt(x) tan(x)

Type

Result
Absolute value of i Cosine of x (x is in radians) Absolute value of x x raised to the power of y Sine of x (x is in radians) Square root of x Tangent of x

int float float float float float float

int float float float float float float

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 - 15 -

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 machinedependent 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. 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:

- 16 -

!20 10 && 5 10 || 5.5 10 && 0

// gives 0 // gives 1 // gives 1 // 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 int sorted = 0; // false 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. Operator ~ & | ^ << >> Name Example Bitwise Negation ~'\011' Bitwise And '\011' & '\027' Bitwise Or '\011' | '\027' Bitwise Exclusive '\011' ^ '\027' Or Bitwise Left Shift '\011' << 2 Bitwise Right Shift '\011' >> 2 Bitwise operators // gives '\366' // gives '\001' // gives '\037' // gives '\036' // gives '\044' // gives '\002'

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.

- 17 -

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 Example x y ~x x&y x|y x^y x << 2 x >> 2

How the bits are calculated. Bit Sequence 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 1 0 1 1 1 0 0 1 1 0 1 1 0 1 1 1 0 1 1 0 0 0

Octal Value 011 027 366 001 037 036 044 002

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: Operator ++ ++ --int k = 5;

Name Auto Increment (prefix) Auto Increment (postfix)

Example ++k + 10 k++ + 10

// gives 16 // gives 15 // gives 14 // gives 15

Auto Decrement (prefix) --k + 10 Auto Decrement (postfix) k-- + 10 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

- 18 -

precedence levels. Operators in higher levels take precedence over operators in lower levels.
Level Highest :: () + ->* * + << < == & ^ | & & || ?: = Lowest , += -= *= /= ^= %= &= |= <<= >>= Binary Left to Right Binary Ternary Binary Left to Right Left to Right Right to Left [] ++ -.* / >> <= != > >= % -> ! ~ . * & new delete Binary Binary Binary Binary Binary Binary Binary Binary Binary Binary Left to Right Left to Right Left to Right Left to Right Left to Right Left to Right Left to Right Left to Right Left to Right Left to Right sizeof() Operator Kind Unary Binary Unary Order Both Left to Right Right to Left

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

- 19 -

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

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; int i = 10.5; i = i + d; // d receives 1.0 // i receives 10 // 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.

- 20 -

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 sideeffect 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). 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. Assignmentlike 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

- 21 -

#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';

} 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;

- 22 -

} Analysis

cout << "Weekly Pay = " << weeklyPay << '\n';

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'; 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 (}). - 23 -

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.
3.1. 3.2.

Control Statements......................................................................................................... 1
INTRODUCTION ............................................................................................................... 1 CONDITIONAL STATEMENTS .......................................................................................... 1 The if Statement .................................................................................................... 1 The switch Statement ............................................................................................ 3

3.2.1. 3.2.2. 3.3.

LOOPING STATEMENTS .................................................................................................. 5 The for Statement ............................................................................................... 6 The while Statement ........................................................................................... 5 The dowhile Statement ................................................................................... 8

3.3.1. 3.3.2. 3.3.3. 3.4.

OTHER STATEMENTS ...................................................................................................... 8 The continue Statement ...................................................................................... 8 The break Statement ........................................................................................... 9 The goto Statement ........................................................................................... 10 The return Statement......................................................................................... 10

3.4.1. 3.4.2. 3.4.3. 3.4.4.

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: 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;

if (expression) statement;

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-

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;

if (ch >= 'A' && ch <= 'Z') kind = upperLetter; else { if (ch >= 'a' && ch <= 'z') kind = lowerLetter; 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 i 1 2 3 4 5 6 While loop trace n i <= n sum 5 1 5 1 5 1 5 1 5 1 5 0

Iteration F i rs t Second Third F o u rt h Fi fth Sixth

+= i++ 1 3 6 10 15

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;

-6-

Contrary to what may appear, the scope for i is not the body of the loop, but the loop itself. Scopewise, the above is equivalent to:
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;) something; // is equivalent to: while (i != 0) // something;

Removing all the expressions gives us an infinite loop. This loop's condition is assumed to be always true:
for (;;) something; // infinite loop

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)

-7-

3.3.3. The dowhile 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 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 loops 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:

-8-

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; // process num here... }

// causes a jump to: ++i

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; // process num here... } //etc... }

// causes a jump to: ++i

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. -9-

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; 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;

- 10 -

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).

- 11 -

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 ........................................................... 13 4.5.1. String Output ............................................................................................. 13 4.5.2. String Input ............................................................................................... 14 4.5.3. Avoiding buffer over flow ........................................................................ 16 4.5.4. String constants ......................................................................................... 16 4.5.5. Copying string the hard way ..................................................................... 16 4.5.6. Copying string the easy way ..................................................................... 17 4.5.7. Concatenating strings ................................................................................ 18 4.5.8. Comparing strings ..................................................................................... 19 4.6. Pointers ............................................................................................................. 19 4.6.1. Dynamic Memory ..................................................................................... 21 4.6.2. Pointer Arithmetic..................................................................................... 22 4.6.3. Function Pointers ...................................................................................... 24 4.6.4. References ................................................................................................. 26 4.6.5. Typedefs .................................................................................................... 28

Chapter Four

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:

sum cin cin cin cin cin cin

= 0.0; >> a; >> b; >> c; >> d; >> e; >> f;

sum sum sum sum sum sum

+= += += += += +=

a; b; c; d; e; 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];

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

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; float sum = 0.0; int count = 0; int nogt10 = 0; // // // // maximum no of elements in array accumulates sum number of elements entered counts no greater than 10%

float x; float indata[NE]; float average; int i;

// // // // //

above average holds each no as input array to hold input average value of input values 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

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> #include <time.h> // time.h and stdlib.h required for // random number generation

void main() { const int die_sides = 6; int count[die_sides]; // int no_trials, // roll, // i; // float sample; //

// maxr-sided die // holds count of each possible value number of trials random integer control variable 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] ;

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];

x = y ;

// Error - Illegal

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 theArray[5][3], the first three elements go int theArray[0]; the next three 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. holds steady. Therefore, if the program has an array int

into theArray[1]; and so forth.

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]; }

12

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 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

13

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 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| cin << s1;

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! 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

14

The name entered was Ian Aitchison Enter a string: Law is a bottomless pit. 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; }

You entered:

Law

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. 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 .

15

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 The best way to understand the true nature of strings is to deal with them character b y 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; }

16

4.5.5. Copying string the easy way


library function will do it for you. You can copy strings using strcpy or strncpy Ofcourse you don't need to use a for loop to copy a string. As you might have guesses, a

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;

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;

17

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.
o o

The first character of the source string is copied to the location of the terminating null character of the destination string. The destination string must have enough space to hold both strings and a terminating null character.
#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); cout << str1 << endl; str1[4] = '\0'; cout << str1 << endl;

strcat(destination, source);

Example:

The function strncat is like strcat except that it copies only a specified number of characters. 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; strncat(destination, source, int n);

18

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
strcmp(str1, str2); returns: < 0 =0 >0
if str1 is less than str2 if str1 is equal to str2 if str1 is greater than str2

Example:

The function strncmp is like strcmp except that it compares only a specified number of characters.
strncmp(str1, str2, int n);

#include <iostream.h> #include <string.h> void main() { cout << strcmp("abc", cout << strcmp("def", cout << strcmp("abc", cout << strcmp("abc", cout << strcmp("abc",

"def") << "abc") << "abc") << "abcdef") "ABC") <<

endl; endl; endl; << endl; endl;

strncmp does not compare characters after a terminating null character has been found in one of the strings. Example:
#include <iostream.h> #include <string.h> void main() { cout << strncmp("abc", cout << strncmp("abc", cout << strncmp("abc", cout << strncmp("abc", cout << strncmp("abc", }

"def", 2) "abcdef", "abcdef", "abcdef", "abcdef",

<< endl; 3) << endl; 2) << endl; 5) << endl; 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:

19

int *ptr1; char *ptr2;

// pointer to an int // 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.


RVT PWO

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. 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).

20

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. 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 [] str; // delete an object // 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.

21

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 2 3 4 5 6 7 #include <string.h> char* CopyOf (const char *str) { char *copy = new char[strlen(str) + 1]; strcpy(copy, str); return copy;

Annotation (analysis)

1 4

This is the standard string header file which declares a variety of functions for manipulating strings. 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. 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.

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.

22

Figure 5.1 Pointer arithmetic.


H E L L O \0 10 20 30 40

str str++

ptr 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 2 3 4 5 Annotation void CopyString (char *dest, char *src) { while (*dest++ = *src++) ; }

Listing 5.3

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.

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.

23

1 2 3 4 5 6 7 8 9 Annotation

int HighestTemp (const int *temp, const int rows, const int columns) { int highest = 0; for (register i = 0; i < rows; ++i) for (register j = 0; j < columns; ++j) if (*(temp + i * columns + j) > highest) highest = *(temp + i * columns + j); return highest;

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. 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 onedimensional array of row * column integers. This is shown in Listing 5.4.
Listing 5.4 1 2 3 4 5 6 7 8 int HighestTemp (const int *temp, const int rows, const int columns) { int highest = 0; for (register i = 0; i < rows * columns; ++i) if (*(temp + i) > highest) highest = *(temp + i); return highest;

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*);

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

24

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"); (*Compare)("Tom", "Tim"); Compare("Tom", "Tim"); // direct call // indirect call // 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 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Annotation int BinSearch (char *item, char *table[], int n, int (*Compare)(const char*, const char*)) { int bot = 0; int top = n - 1; int mid, cmp; while (bot <= top) { mid = (bot + top) / 2; if ((cmp = Compare(item,table[mid])) == 0) return mid; // return item index else if (cmp < 0) top = mid - 1; // restrict search to lower half else bot = mid + 1; // restrict search to upper half } return -1; // not found

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. 25

2 7

Compare is the function pointer to be used for comparing item against the

array elements. 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. The item is compared against the middle item of the array.

10 If item matches the middle item, the latters 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.
strcmp passed as the comparison function:

The following example shows how BinSearch may be called with


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,
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; num3 = num1; // illegal: reference without an initializer

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.

26

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 1s 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 1s. 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 1 2 3 4 5 6 7 8 9 10 11 12 void Swap1 (int x, int y) { int temp = x; x = y; y = temp; } void Swap2 (int *x, int *y) { int temp = *x; *x = *y; *y = temp; } // pass-by-value (objects)

// pass-by-value (pointers)

13 void Swap3 (int &x, int &y) 14 { 15 int temp = x; 16 x = y; 17 y = temp; 18 } Annotation

// pass-by-reference

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.
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.

27

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 = Swap1(i, j); Swap2(&i, &j); Swap3(i, j); }

20; cout << i << ", " << j << '\n'; cout << i << ", " << j << '\n'; 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 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; Namename; uintn; // is the same as: char *str; // is the same as: char name[12]; // 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 BinSearchs signature arguably simpler.

28

Chapter five Modular Programming


5.1. 5.2. Definition of Functions ....................................................................................... 1 Declaring and Defining Functions .............................................................. 1 Local Variables ........................................................................................... 7 Global Variables ......................................................................................... 8 Scope of variables ............................................................................................... 7

5.1.1. 5.2.1. 5.2.2. 5.3. 5.4.

Function Arguments.......................................................................................... 12 Passing arguments ............................................................................................. 13 Pass by Value ............................................................................................ 13 Pass by reference....................................................................................... 15

5.4.1. 5.4.2. 5.5. 5.6. 5.7. 5.8.

Return Values.................................................................................................... 16 Default Parameters ............................................................................................ 18 Inline Functions ................................................................................................ 20 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:
x

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. x x 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. x 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);

-2-

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); 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); void PrintMessage(int messageNumber); int GetChoice(); BadFunction(); 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: -3// returns long, has two parameters // returns void, has one parameter // returns int, has no parameters // returns int, has no parameters

Listing 5.1 demonstrates a program that includes a function prototype for the Area()

20: cout << "\nYour yard is "; 21: cout << areaOfYard; 22: cout << " square feet\n\n"; 23: return 0; 24: } 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, lengthOfYard

the FindArea() function would have used the value in widthOfYard for length and 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.

-4-

Function Definition Syntax return_type function_name ( [type parameterName1] , [type parameterName2] ...) { statements; } A function prototype tells the compiler the return type, name, and parameter list. Functions 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

-5-

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 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.

-6-

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 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: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: #include <iostream.h> float Convert(float); int main() { float TempFer; float TempCel; cout << "Please enter the temperature in Fahrenheit: "; cin >> TempFer; TempCel = Convert(TempFer); cout << "\nHere's the temperature in Celsius: "; cout << TempCel << endl; return 0; } float Convert(float TFer) { float TCel; TCel = ((TFer - 32) * 5) / 9; return TCel;

-7-

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(). 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. 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

Global variables have global scope and are available anywhere within your program. 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 -8-

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(); 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(). -9-

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.

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: }

- 10 -

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 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. Global variables 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 indicate to the variable name made within the scope of the local variable. This situation is illustrated in the following program.

- 11 -

# 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 As shown by the above output, the local variable name takes precedence over 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

- 12 -

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. 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); unsigned long squared = square(cubed); unsigned long tripled = triple(squared); unsigned long Answer = double(tripled); // cubed = 8 // squared = 64 // tripled = 196 // 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: 2: 3: 4: // Listing 5.5 - demonstrates passing by value #include <iostream.h>

- 13 -

5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: }

void swap(int x, int y); int main() { int x = 5, y = 10; cout << "Main. Before swap, x: " << x << " y: " <<y<< "\n"; swap(x,y); cout << "Main. After swap, x: " << x << " y: " <<y << "\n"; return 0; } void swap (int x, int y) { int temp; cout << "Swap. Before swap, x: " << x << " y: " <<y<< "\n"; temp = x; x = y; y = temp; cout << "Swap. After swap, x: " << x << " y: " << y << "\n";

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.

- 14 -

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.

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: }

- 15 -

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. 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.

- 16 -

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: "; 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

- 17 -

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 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)

- 18 -

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.


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);

Listing 5.7. A demonstration of default parameter values.

- 19 -

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 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.

- 20 -

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; } 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:

- 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 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
target = 2 * target;

you had written the following: 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 H Z

- 22 -

From this definition, f(3) = 2f(2) + 32 = 2f(2) + 9 f(2) = 2f(1) + 2 = 2f(1) + 4 f(1) = 2f(0) + 12 = 2f(0) + 1 f(0) = 0 f(1) = 2 (0) + 1 =0+1 =1 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 t 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 now we should compute f(0) first.
2

now we should compute f(2) first. now we should compute f(1) first.

- 23 -

f (N )

Nx f(N - 1) for N ! 0 for N 0 1

this is called recursion.

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; } //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) f(i - 1)  f(i - 2) 1 for i ! 2 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. - 24 -

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 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.

gcd (M, N)

if N M N if M ! N gcd (N, M) 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

- 25 -

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 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

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:

- 26 -

#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); } } int main () { ch a r F ; int N; cout<<"How many disks ?=====>";cin>>N; Form(N,'A','B','C') ; getch () ; return 0; }

- 27 -

You might also like