Department of CS Computer Programming - I Chapter One 1.1 Introduction to programming A Computer is an electronic device that accepts data, performs computations, and makes logical decisions according to instructions that have been given to it; then produces meaningful information in a form that is useful to the user. In current world we live in, computers are almost used in all walks of life for different purposes. They have been deployed to solve different real life problems, from the simplest game playing up to the complex nuclear energy production. Computers are important and widely used in our society because they are cost-effective aids to problem solving in business, government, industry, education, etc. In order to solve a given problem, computers must be given the correct instruction about how they can solve it. The terms computer programs, software programs, or just programs are the instructions that tells the computer what to do. Computer requires programs to function, and a computer programs does nothing unless its instructions are executed by a CPU. Computer programming (often shortened to programming or coding) is the process of writing, testing, debugging/troubleshooting, and maintaining the source code of computer programs. Writing computer programs means writing instructions that will make the computer follow and run a program based on those instructions. Each instruction is relatively simple, yet because of the computer's speed, it is able to run millions of instructions in a second. A computer program usually consists of two elements: Data – characteristics Code – action+ A computer program (also known as source code) is often written by professionals known as Computer Programmers (simply programmers). Source code is written in one of programming languages. A programming language is an artificial language that can be used to control the behavior of a machine, particularly a computer. Programming languages, like natural language (such as English), 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 Page 1 Department of CS Computer Programming - I tasks. The combination of the language definition, the program, and the program's inputs must fully specify the external behavior that occurs when the program is executed. Computer languages have relatively few, exactly defined, rules for composition of programs, and strictly controlled vocabularies in which unknown words must be defined before they can be used. Available programming languages come in a variety of forms and types. Thousands of different programming languages have been developed, used, and discarded. Programming languages can be divided in to two major categories: Low-level languages High-level languages. Low-level languages Computers only understand one language and that is binary language or the language of 1s and 0s. Binary language is also known as machine language, one of low-level languages. In the initial years of computer programming, all the instructions were given in binary form. Although the computer easily understood these programs, it proved too difficult for a normal human being to remember all the instructions in the form of 0s and 1s. Therefore, computers remained mystery to a common person until other languages such as assembly language was developed, which were easier to learn and understand. Assembly language correspondences symbolic instructions and executable machine codes and was created to use letters (called mnemonics) to each machine language instructions to make it easier to remember or write. For example: ADD A, B – adds two numbers in memory location A and B Assembly language is nothing more than a symbolic representation of machine code, which allows symbolic designation of memory locations. However, no matter how close assembly language is to machine code, computers still cannot understand it. The assembly language must be translated to machine code by a separate program called assembler. The machine instruction created by the assembler from the original program (source code) is called object code. Thus assembly languages are unique to a specific computer (machine). Assemblers are written for each unique machine language. High-level languages Although programming in assembly language is not as difficult and error prone as stringing together ones and zeros, it is slow and cumbersome. In addition it is hardware specific. The lack of portability between different computers led to the development of high-level languages—so called because they permitted a programmer to ignore many low-level details of the computer's hardware. Further, it was recognized that the closer the syntax, rules, and mnemonics of the programming language could be to "natural language" the less likely it became that the programmer would inadvertently introduce errors (called "bugs") into the program. High-level languages are more English-like and, therefore, make it easier for programmers to "think" in the programming language. Highlevel 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 Page 2 Department of CS Computer Programming - I (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 of code execution. The most influential paradigms are examined in the next three sections, in approximate chronological order. Procedural Programming Languages Procedural programming specifies a list of operations that the program must complete to reach the desired state. Each program has a starting state, a list of operations to complete, and an ending point. This approach is also known as imperative programming. Integral to the idea of procedural programming is the concept of a procedure call. Procedures, also known as functions, subroutines, or methods, are small sections of code that perform a particular function. A procedure is effectively a list of computations to be carried out. Procedural programming can be compared to unstructured programming, where all of the code resides in a single large block. By splitting the programmatic tasks into small pieces, procedural programming allows a section of code to be re-used in the program without making multiple copies. It also makes it easier for programmers to understand and maintain program structure. Two of the most popular procedural programming languages are FORTRAN and BASIC. Structured Programming Languages Structured programming is a special type of procedural programming. It provides additional tools to manage the problems that larger programs were creating. Structured programming requires that programmers break program structure into small pieces of code that are easily understood. It also frowns upon the use of global variables and instead uses variables local to each subroutine. One of the well-known features of structural programming is that it does not allow the use of the GOTO statement. It is often associated with a "top-down" approach to design. The top-down approach begins with an initial overview of the system that contains minimal details about the different parts. Subsequent design iterations then add increasing detail to the components until the design is complete. The most popular structured programming languages include C, Ada, and Pascal. Object-Oriented Programming Languages Object-oriented programming is one the newest and most powerful paradigms. In objectoriented programs, the designer specifies both the data structures and the types of Page 3 Department of CS Computer Programming - I 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. 1.2 Problem solving Techniques Computer solves varieties of problems that can be expressed in a finite number of steps leading to a precisely defined goal by writing different programs. A program is not needed only to solve a problem but also it should be reliable, (maintainable) portable and efficient. In computer programming two facts are given more weight: The first part focuses on defining the problem and logical procedures to follow in solving it. The second introduces the means by which programmers communicate those procedures to the computer system so that it can be executed. There are system analysis and design tools, particularly flowchart and structure chart, that can be used to define the problem in terms of the steps to its solution. The programmer uses programming language to communicate the logic of the solution to the computer. Before a program is written, the programmer must clearly understand what data are to be used, the desired result, and the procedure to be used to produce the result. The procedure, or solution, selected is referred to as an algorithm. An algorithm is defined as a step-by-step sequence of instructions that must terminate and describe how the data is to be processed to produce the desired outputs. Simply, algorithm is a sequence of instructions. Algorithms are a fundamental part of computing. There are three commonly used tools to help to document program logic (the algorithm). These are flowcharts, structured chart, and 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 Page 4 Department of CS Computer Programming - I break the main tasks that must be accomplished into smaller ones in order to be able to eventually write fully developed code. Writing pseudocode will save you time later during the construction & testing phase of a program's development. Example: Original Program Specification: Write a program that obtains two integer numbers from the user. It will print out the sum of those numbers. Pseudocode: Prompt the user to enter the first integer Prompt the user to enter a second integer Compute the sum of the two user inputs Display an output prompt that explains the answer as the sum Display the result 1.2.2 Structured Charts Structured chart depicts the logical functions to the solution of the problem using a chart. It provides an overview that confirms the solution to the problem without excessive consideration to detail. It is high-level in nature. Example: Write a program that asks the user to enter a temperature reading in centigrade and then prints the equivalent Fahrenheit value. Input Centigrade Process Prompt for centigrade value Read centigrade value Compute Fahrenheit value Display Fahrenheit value Output Fahrenheit CelsusToFarh (main func) centigard InPutCen centigard CalcFar Fahrenheit OutPutFar Fahrenheit Page 5 Department of CS Computer Programming - I 1.2.3 Flowchart A flowchart (also spelled flow-chart and flow chart) is a schematic representation of an algorithm or a process. The advantage of flowchart is it doesn‘t depend on any particular programming language, so that it can used, to translate an algorithm to more than one programming language. Flowchart uses different symbols (geometrical shapes) to represent different processes. The following table shows some of the common symbols. Example 1: - Draw flow chart of an algorithm to add two numbers and display their result. Algorithm description Read the rules of the two numbers (A and B) Add A and B Assign the sum of A and B to C Display the result ( c) The flow chart is: Page 6 Department of CS Computer Programming - I 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 Page 7 Department of CS Computer Programming - I Sometimes 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 Page 8 Department of CS Computer Programming - I 1.3 System Development Life Cycle (SDLC) The Systems Development Life Cycle (SDLC) is a conceptual model used in project management that describes the stages involved in a computer system development project from an initial feasibility study through maintenance of the completed application. The phases of SDLC are discussed below briefly. 1.3.1 Feasibility study The first step is to identify a need for the new system. This will include determining whether a business problem or opportunity exists, conducting a feasibility study to determine if the proposed solution is cost effective, and developing a project plan. This process may involve end users who come up with an idea for improving their work or may only involve IS people. Ideally, the process occurs in tandem with a review of the organization's strategic plan to ensure that IT is being used to help the organization achieve its strategic objectives. Management may need to approve concept ideas before any money is budgeted for its development. A preliminary analysis, determining the nature and scope of the problems to be solved is carried out. Possible solutions are proposed, describing the cost and benefits. Finally, a preliminary plan for decision making is produced. The process of developing a large information system can be very costly, and the investigation stage may require a preliminary study called a feasibility study, which includes e.g. the following components: a. Organizational Feasibility How well the proposed system supports the strategic objectives of the organization. b. Economic Feasibility Cost savings Increased revenue Decreased investment Increased profits Page 9 Department of CS Fundamental of Programming - I c. Technical Feasibility Hardware, software, and network capability, reliability, and availability d. Operational Feasibility End user acceptance Management support Customer, supplier, and government requirements 1.3.2 Requirements analysis Requirements analysis is the process of analyzing the information needs of the end users, the organizational environment, and any system presently being used, developing the functional requirements of a system that can meet the needs of the users. Also, the requirements should be recorded in a document, email, user interface storyboard, executable prototype, or some other form. The requirements documentation should be referred to throughout the rest of the system development process to ensure the developing project aligns with user needs and requirements. End users must be involved in this process to ensure that the new system will function adequately and meets their needs and expectations. 1.3.3 Designing solution After the requirements have been determined, the necessary specifications for the hardware, software, people, and data resources, and the information products that will satisfy the functional requirements of the proposed system can be determined. The design will serve as a blueprint for the system and helps detect problems before these errors or problems are built into the final system. The created system design, but must reviewed by users to ensure the design meets users' needs. 1.3.4 Testing designed solution A smaller test system is sometimes a good idea in order to get a ―proof-of-concept‖ validation prior to committing funds for large scale fielding of a system without knowing if it really works as intended by the user. 1.3.5 Implementation The real code is written here. Systems implementation is the construction of the new system and its delivery into production or day-to-day operation.The key to understanding the implementation phase is to realize that there is a lot more to be done than programming. Implementation requires programming, but it also requires database creation and population, and network installation and testing. You also need to make sure the people are 10 Department of CS Fundamental of Programming - I taken care of with effective training and documentation. Finally, if you expect your development skills to improve over time, you need to conduct a review of the lessons learned. 1.3.6 Unit testing Normally programs are written as a series of individual modules, these subject to separate and detailed test. 1.3.7 Integration and System testing Bring 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. Chapter Two 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. 11 Department of CS Fundamental of Programming - I Writing a Program To write a source code, your compiler may have its own built-in text editor, or you may be using a commercial text editor or word processor that can produce text files. The important thing is that whatever you write your program in, it must save simple, plain-text files, with no word processing commands embedded in the text. Examples of safe editors include Windows Notepad, the DOS Edit command, EMACS, and VI (for Linux). Many commercial word processors, such as WordPerfect, Word, and dozens of others, also offer a method for saving simple text files. The files you create with your editor are called source files, and for C++ they typically are named with the extension .CPP Compiling Your source code file can't be executed, or run, as a program can. To turn your source code into a program, you use a compiler. How you invoke your compiler, and how you tell it where to find your source code, will vary from compiler to compiler; check your documentation. In Borland's Turbo C++ you pick the RUN menu command or type tc <filename> from the command line, where <filename> is the name of your source code file (for example, test.cpp). Other compilers may do things slightly differently. After your source code is compiled, an object file is produced. This file is often named with the extension .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 12 Department of CS Fundamental of Programming - I adding two numbers or printing to the screen. A class is a collection of data and related functions. Summary The steps to create an executable file are 1. Create a source code file, with a .CPP extension. 2. Compile the source code into a file with the .OBJ extension. 3. Link your OBJ file with any needed libraries to produce an executable program. 2.3. Showing Sample program Any meaningful program written in C++ has to contain a number of components: the main function; some variable declarations; and some executable statements. For example, the following is a very basic C++ program: 1: #include <iostream.h> 2: 3: int main() 4: { 5: cout << "Hello World!\n"; 6: return 0; 7: } On line 1, the file iostream.h is included in the file. The first character is the # symbol, which is a signal to the preprocessor. Each time you start your compiler, the preprocessor is run. The preprocessor reads through your source code, looking for lines that begin with the pound symbol (#), and acts on those lines before the compiler runs. include is a preprocessor instruction that says, "What follows is a filename. Find that file and read it in right here." The angle brackets around the filename tell the preprocessor to 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. 13 Department of CS Fundamental of Programming - I The preprocessor runs before your compiler each time the compiler is invoked. The preprocessor translates any line that begins with a pound symbol (#) into a special command, getting your code file ready for the compiler. Line 3 begins the actual program with a function named main(). Every C++ program has a main() function. In general, a function is a block of code that performs one or more actions. Usually functions are invoked or called by other functions, but main() is special. When your program starts, main() is called automatically. main(), like all functions, must state what kind of value it will return. The return value type for main() in HELLO.CPP is int, which means that this function will return an integer value. All functions begin with an opening brace ({) and end with a closing brace (}). The braces for the main() function are on lines 4 and 7. Everything between the opening and closing braces is considered a part of the function. The meat and potatoes of this program is on line 5. The object cout is used to print a message to the screen. cout is used in C++ to print strings and values to the screen. A string is just a set of characters. Here's how cout is used: type the word cout, followed by the output redirection operator (<<). Whatever follows the output redirection operator is written to the screen. If you want a string of characters written, be sure to enclose them in double quotes ("), as shown on line 5. A text string is a series of printable characters. The final two characters, \n, tell cout to put a new line after the words Hello World! All ANSI-compliant programs declare main() to return an int. This value is "returned" to the operating system when your program completes. Some programmers signal an error by returning the value 1. The main() function ends on line 7 with the closing brace. 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 lowercase letters. The following are some of the reserved words of C++. 14 Department of CS Asm const_cast dynamic_cast Explicit Goto namespace reinterpret_cast static_cast Throw Union wchar_t Fundamental of Programming - I auto class do extern if new register static true unsigned Bool Const Double False Inline Operator Return struct Try Using break char delete float int private short switch typedef virtual case continue else for long 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 Syntax of an identifier Letter _ Digit - For the purposes of C++ identifiers, the underscore symbol, _, is considered to be a letter. Its use as the first character in an identifier is not recommended though, because many library functions in C++ use such identifiers. Similarly, the use of two consecutive underscore symbols, _ _, is forbidden. The following are valid identifiers 15 Department of CS Fundamental of Programming - I Length days_in_year DataSet1 Int _Pressure first_one A l t ho ug h us i n g _ Pre s su r e i s n ot r e c omm e nd e d . Profit95 first_1 The following are invalid: days-in-year throw 1data No## int bestWish! first.val Although it may be easier to type a program consisting of single character identifiers, modifying or correcting the program becomes more and more difficult. The minor typing effort of using meaningful identifiers will repay itself many fold in the avoidance of simple programming errors when the program is modified. At this stage it is worth noting that C++ is case-sensitive. That is lower-case letters are treated as distinct from upper-case letters. Thus the word NUM different from the word num or the word Num. Identifiers can be used to identify variable or constants or functions. Function identifier is an identifier that is used to name a function. 2.4.3. Literals Literals are constant values which can be a number, a character of a string. For example the number 129.005, the character ‗A‘ and the string ―hello world‖ are all literals. There is no identifier that identifies them. 2.4.4. Comments A comment is a piece of descriptive text which explains some aspect of a program. Program comments are totally ignored by the compiler and are only intended for human readers. C++ provides two types of comment delimiters: Anything after // (until the end of the line on which it appears) is considered a comment. Anything enclosed by the pair /* and */ is considered a comment. 16 Department of CS Fundamental of Programming - I 2.5. Data Types, Variables, and Constants 2.5.1. Variables A variable is a symbolic name for a memory location in which data can be stored and subsequently recalled. Variables are used for holding data values so that they can be utilized in various computations in a program. All variables have two important attributes: A type, which is, established when the variable is defined (e.g., integer, float, character). Once defined, the type of a C++ variable cannot be changed. A value, which can be changed by assigning a new value to the variable. The kind of values a variable can assume depends on its type. For example, an integer variable can only take integer values (e.g., 2, 100, -12) not real numbers like 0.123. Variable Declaration Declaring a variable means defining (creating) a variable. You create or define a variable by stating its type, followed by one or more spaces, followed by the variable name and a semicolon. The variable name can be virtually any combination of letters, but cannot contain spaces and the first character must be a letter or an underscore. Variable names cannot also be the same as keywords used by C++. Legal variable names include x, J23qrsnf, and myAge. Good variable names tell you what the variables are for; using good names makes it easier to understand the flow of your program. The following statement defines an integer variable called myAge: int myAge; IMPORTANT- Variables must be declared before used! As a general programming practice, avoid such horrific names as J23qrsnf, and restrict single-letter variable names (such as x or i) to variables that are used only very briefly. Try to use expressive names such as myAge or howMany. A point worth mentioning again here is that C++ is case-sensitive. In other words, uppercase and lowercase letters are considered to be different. A variable named age is different from Age, which is different from AGE. 17 Department of CS Fundamental of Programming - I 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. 18 Department of CS Fundamental of Programming - I 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. Type Size 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 2 bytes 2 bytes 4 bytes 4 bytes 2 bytes 2 bytes 2 bytes 1 byte 4 bytes 8 bytes 10 bytes Values 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 Table C++ data types and their ranges 19 Department of CS Fundamental of Programming - I 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. Example: A demonstration of the use of variables. 2: #include <iostream.h> 3: 4: int main() 5: { 6: unsigned short int Width = 5, Length; 7: Length = 10; 8: 9: 10: 11: // create an unsigned short and initialize with result // of multiplying Width by Length unsigned short int Area = Width * Length; 12: 13: cout << "Width:" << Width << "\n"; 14: cout << "Length: " << Length << endl; 15: cout << "Area: " << Area << endl; 16: return 0; 17: } Output: Width:5 Length: 10 Area: 50 Line 2 includes the required include statement for the iostream's library so that cout will work. Line 4 begins the program. 20 Department of CS Fundamental of Programming - I 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. Wrapping around integer values The fact that unsigned long integers have a limit to the values they can hold is only rarely a problem, but what happens if you do run out of room? When an unsigned integer reaches its maximum value, it wraps around and starts over, much as a car odometer might. The following example shows what happens if you try to put too large a value into a short integer. Example: A demonstration of putting too large a value in a variable 1: #include <iostream.h> 2: int main() 3: { 4: unsigned short int smallNumber; 5: smallNumber = 65535; 6: cout << "small number:" << smallNumber << endl; 7: smallNumber++; 8: cout << "small number:" << smallNumber << endl; 9: smallNumber++; 10: cout << "small number:" << smallNumber << endl; 11: return 0; 12: } Output: small number:65535 small number:0 small number:1 A signed integer is different from an unsigned integer, in that half of the values you can represent are negative. Instead of picturing a traditional car odometer, you might picture one that rotates up for positive numbers and down for negative numbers. One mile from 0 21 Department of CS Fundamental of Programming - I 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 adding too large a number to a signed integer. 1: #include <iostream.h> 2: int main() 3: { 4: short int smallNumber; 5: smallNumber = 32767; 6: cout << "small number:" << smallNumber << endl; 7: smallNumber++; 8: cout << "small number:" << smallNumber << endl; 9: smallNumber++; 10: cout << "small number:" << smallNumber << endl; 11: return 0; 12: } Output: small number:32767 small number:-32768 small number:-32767 IMPORTANT – To any variable, do not assign a value that is beyond its range! 2.5.4. Characters Character variables (type char) are typically 1 byte, enough to hold 256 values. A char can be interpreted as a small number (0-255) or as a member of the ASCII set. ASCII stands for the American Standard Code for Information Interchange. The ASCII character set and its ISO (International Standards Organization) equivalent are a way to encode all the letters, numerals, and punctuation marks. In the ASCII code, the lowercase letter "a" is assigned the value 97. All the lower- and uppercase letters, all the numerals, and all the punctuation marks are assigned values between 1 and 128. Another 128 marks and symbols are reserved for use by the computer maker, although the IBM extended character set has become something of a standard. 22 Department of CS Fundamental of Programming - I 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 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. Operator = Example n = 25 Equivalent To 23 Department of CS += -= *= /= %= &= |= ^= <<= >>= Fundamental of Programming - I n += 25 n -= 25 n *= 25 n /= 25 n %= 25 n &= 0xF2F2 n |= 0xF2F2 n ^= 0xF2F2 n <<= 4 n >>= 4 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; 2.6.2. Arithmetic Operators 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). 24 Department of CS Fundamental of Programming - I 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 cost = 100; int volume = 80; double unitPrice = cost / (double) volume; // gives 1.25 The remainder operator (%) expects integers for both of its operands. It returns the remainder of integer-dividing the operands. For example 13%3 is calculated by integer dividing 13 by 3 to give an outcome of 4 and a remainder of 1; the result is therefore 1. It is possible for the outcome of an arithmetic operation to be too large for storing in a designated variable. This situation is called an overflow. The outcome of an overflow is machine-dependent and therefore undefined. For example: unsigned char k = 10 * 92; // overflow: 920 > 255 It is illegal to divide a number by zero. This results in a run-time division-by-zero failure, which typically causes the program to terminate. There are also a number of predefined library functions, which perform arithmetic operations. As with input & output statements, if you want to use these you must put a #include statement at the start of your program. Some of the more common library functions are summarized as Parameter 1.6 Result Type(s) 1.4 Header File 1.5 Function <stdlib.h> abs(i) int int <math.h> cos(x) float float <math.h> fabs(x) float float Type 25 follow 1.7 Result Absolute value of i Cosine of x (x is in radians) Absolute value of Department of CS Fundamental of Programming - I <math.h> pow(x, y) float float <math.h> sin(x) float float <math.h> <math.h> sqrt(x) tan(x) float float float float x x raised to the power of y Sine of x (x is in radians) Square root of x Tangent of x 2.6.3. Relational Operators C++ provides six relational operators for comparing numeric quantities. These are summarized in table below. Relational operators evaluate to 1 (representing the true outcome) or 0 (representing the false outcome). Operator == != < <= > >= Name Example Equality 5 == 5 // gives 1 Inequality 5 != 5 // gives 0 Less Than 5 < 5.5 // gives 1 Less Than or Equal 5 <= 5 // gives 1 Greater Than 5 > 5.5 // gives 0 Greater Than or Equal 6.3 >= 5 // gives 1 Relational operators Note that the <= and >= operators are only supported in the form shown. In particular, =< and => are both invalid and do not mean anything. The operands of a relational operator must evaluate to a number. Characters are valid operands since they are represented by numeric values. For example (assuming ASCII coding): 'A' < 'F' // gives 1 (is like 65 < 70) The relational operators should not be used for comparing strings, because this will result in the string addresses being compared, not the string contents. For example, the expression "HELLO" < "BYE" causes the address of "HELLO" to be compared to the address of "BYE". As these addresses are determined by the compiler (in a machine-dependent manner), the outcome may be 0 or 1, and is therefore undefined. C++ provides library functions (e.g., strcmp) for the lexicographic comparison of string. 26 Department of CS Fundamental of Programming - I 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 Logical Negation Logical And Logical Or Example !(5 == 5) // gives 0 5 < 6 && 6 < 6 // gives 1 5 < 6 || 6 < 5 // gives 1 Logical negation is a unary operator, which negates the logical value of its single operand. If its operand is nonzero it produces 0, and if it is 0 it produces 1. Logical and produces 0 if one or both of its operands evaluate to 0. Otherwise, it produces 1. Logical or produces 0 if both of its operands evaluate to 0. Otherwise, it produces 1. Note that here we talk of zero and nonzero operands (not zero and 1). In general, any nonzero value can be used to represent the logical true, whereas only zero represents the logical false. The following are, therefore, all valid logical expressions: !20 10 && 5 10 || 5.5 10 && 0 // gives 0 // gives 1 // gives 1 // gives 0 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 ~n & | ^ << >> Name Example Bitwise Negation ~'\011' // gives '\366' Bitwise And '\011' & '\027' // gives '\001' Bitwise Or '\011' | '\027' // gives '\037' Bitwise Exclusive '\011' ^ '\027' // gives '\036' Or Bitwise Left Shift '\011' << 2 // gives '\044' Bitwise Right Shift '\011' >> 2 // gives '\002' Bitwise operators 27 Department of CS Fundamental of Programming - I Bitwise operators expect their operands to be integer quantities and treat them as bit sequences. Bitwise negation is a unary operator which reverses the bits in its operands. Bitwise and compares the corresponding bits of its operands and produces a 1 when both bits are 1, and 0 otherwise. Bitwise or compares the corresponding bits of its operands and produces a 0 when both bits are 0, and 1 otherwise. Bitwise exclusive or compares the corresponding bits of its operands and produces a 0 when both bits are 1 or both bits are 0, and 1 otherwise. Bitwise left shift operator and bitwise right shift operator both take a bit sequence as their left operand and a positive integer quantity n as their right operand. The former produces a bit sequence equal to the left operand but which has been shifted n bit positions to the left. The latter produces a bit sequence equal to the left operand but which has been shifted n bit positions to the right. Vacated bits at either end are set to 0. Table 2.1 illustrates bit sequences for the sample operands and results in Table 2.Error! Bookmark not defined.. To avoid worrying about the sign bit (which is machine dependent), it is common to declare a bit sequence as an unsigned quantity: unsigned char x = '\011'; unsigned char y = '\027'; Table 2.1 Example X Y ~x x&y x|y x^y x << 2 x >> 2 How the bits are calculated. Octal Value 011 027 366 001 037 036 044 002 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 2.6.6. Increment/decrement Operators The auto increment (++) and auto decrement (--) operators provide a convenient way of, respectively, adding and subtracting 1 from a numeric variable. These are summarized in the following table. The examples assume the following variable definition: int k = 5; Operator Name Example 28 Department of CS Fundamental of Programming - I ++ ++ Auto Increment (prefix) Auto Increment (postfix) ++k + 10 k++ + 10 --- Auto Decrement (prefix) --k + 10 Auto Decrement (postfix) k-- + 10 Increment and decrement operators // gives 16 // gives 15 // gives 14 // gives 15 Both operators can be used in prefix and postfix form. The difference is significant. When used in prefix form, the operator is first applied and the outcome is then used in the expression. When used in the postfix form, the expression is evaluated first and then the operator applied. Both operators may be applied to integer as well as real variables, although in practice real variables are rarely useful in this form. 2.7. Precedence of Operators The order in which operators are evaluated in an expression is significant and is determined by precedence rules. These rules divide the C++ operators into a number of precedence levels. Operators in higher levels take precedence over operators in lower levels. Level Highest Operator :: Kind Order Unary Both Binary Left to Right Unary Right to Left Binary Left to Right Binary Left to Right () [] -> . + ++ ! * new - -- ~ & delete ->* .* * / + - Binary Left to Right << >> Binary Left to Right < <= Binary Left to Right == != Binary Left to Right & Binary Left to Right ^ Binary Left to Right | Binary Left to Right & Binary Left to Right Binary Left to Right % > >= sizeof() & || 29 Department of CS Fundamental of Programming - I ?: = Lowest += *= ^= &= <<= -= /= %= = >>= , Ternary Left to Right Binary Right to Left Binary Left to Right For example, in a == b + c * d c * d is evaluated first because * has a higher precedence than + and ==. The result is then added to b because + has a higher precedence than ==, and then == is evaluated. Precedence rules can be overridden using brackets. For example, rewriting the above expression as a == (b + c) * d causes + to be evaluated before *. Operators with the same precedence level are evaluated in the order specified by the last column of Table 2.7. For example, in a = b += c 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: 30 Department of CS int(3.14) Fundamental of Programming - I // 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. 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. 31 Department of CS Fundamental of Programming - I Like many other procedural languages, C++ provides different forms of statements for different purposes. Declaration statements are used for defining variables. Assignment-like statements are used for simple, algebraic computations. Branching statements are used for specifying alternate paths of execution, depending on the outcome of a logical condition. Loop statements are used for specifying computations which need to be repeated until a certain logical condition is satisfied. Flow control statements are used to divert the execution path to another part of the program. We will discuss these in turn. 2.9.1. Input/Output Statements The most common way in which a program communicates with the outside world is through simple, character-oriented Input/Output (IO) operations. C++ provides two useful operators for this purpose: >> for input and << for output. We have already seen examples of output using <<. Example 2.1 also illustrates the use of >> for input. Example #include <iostream.h> int main (void) { int workDays = 5; float workHours = 7.5; float payRate, weeklyPay; cout << "What is the hourly pay rate? "; cin >> payRate; weeklyPay = workDays * workHours * payRate; cout << "Weekly Pay = "; cout << weeklyPay; cout << '\n'; } Analysis This line outputs the prompt ‗What is the hourly pay rate? ‘ to seek user input. 32 Department of CS Fundamental of Programming - I 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 float workDays = 5; workHours, payRate, weeklyPay; cout << "What are the work hours and the hourly pay rate? "; cin >> workHours >> payRate; weeklyPay = workDays * workHours * payRate; cout << "Weekly Pay = " << weeklyPay << '\n'; } Analysis This line reads two input values typed by the user and copies them to workHours and payRate, respectively. The two values should be separated by white space (i.e., one or more space or tab characters). This statement is equivalent to: (cin >> workHours) >> payRate; 33 Department of CS Fundamental of Programming - I 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 (}). 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. 34 Department of CS Fundamental of Programming - I 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; 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. 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. 3.2. Conditional Statements 35 Department of CS Fundamental of Programming - I 3.2.1. The if Statement It is sometimes desirable to make the execution of a statement dependent upon a condition being satisfied. The if statement provides a way of expressing this, the general form of which is: if (expression) 36 Department of CS Fundamental of Programming - I statement; First expression is evaluated. If the outcome is nonzero (true) then statement is executed. Otherwise, nothing happens. For example, when dividing two values, we may want to check that the denominator is nonzero: if (count != 0) average = sum / count; To make multiple statements dependent on the same condition, we can use a compound statement: if (balance > 0) { interest = balance * creditRate; balance += interest; } 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: 37 Department of CS Fundamental of Programming - I 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 { if (ch >= 'A' && ch <= 'Z') kind = upperLetter; else { if (ch >= 'a' && ch <= 'z') kind = lowerLetter; else kind = special; } } For improved readability, it is conventional to format such cases as follows: if (ch >= '0' && ch <= '9') kind = digit; else if (cha >= 'A' && ch <= 'Z') kind = capitalLetter; else if (ch >= 'a' && ch <= 'z') kind = smallLetter; else kind = special; 3.2.2. The switch Statement The switch statement provides a way of choosing between a set of alternatives, based on the value of an expression. The general form of the switch statement is: switch (expression) { case constant1: statements; ... case constantn: statements; default: statements; } 38 Department of CS Fundamental of Programming - I 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. 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; } 39 Department of CS Fundamental of Programming - I 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 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 flavours 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: 40 Department of CS Fundamental of Programming - I 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. Iteration First Second Third Fourth Fifth Sixth Table 5.1 i 1 2 3 4 5 6 While loop trace n i <= n sum += i++ 5 1 1 5 1 3 5 1 6 5 1 10 5 1 15 5 0 It is not unusual for a while loop to have an empty body (i.e., a null statement). 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; } 41 Department of CS Fundamental of Programming - I The most common use of for loops is for situations where a variable is incremented or decremented with every iteration of the loop. The following for loop, for example, calculates the sum of all integers from 1 to n. sum = 0; for (i = 1; i <= n; ++i) { sum += i; } This is preferred to the while-loop version we saw earlier. In this example, i is usually called the loop variable. C++ allows the first expression in a for loop to be a variable definition. In the above loop, for example, i can be defined inside the loop itself: for (int i = 1; i <= n; ++i) { sum += i; } Contrary to what may appear, the scope for i is not the body of the loop, but the loop itself. Scope-wise, the above is equivalent to: 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; 42 Department of CS Fundamental of Programming - I Because loops are statements, they can appear inside other loops. In other words, loops can be nested. For example, for (int i = 1; i <= 3; ++i) for (int j = 1; j <= 3; ++j) cout << '(' << i << ',' << j << ")\n"; produces the product of the set {1,2,3} with itself, giving the output: (1,1) (1,2) (1,3) (2,1) (2,2) (2,3) (3,1) (3,2) (3,3) 3.3.3. The ‘do…while’ Statement The do statement (also called do loop) is similar to the while statement, except that its body is executed first and then the loop condition is examined. The general form of the do statement is: do statement; while (expression); First statement is executed and then expression is evaluated. If the outcome of the latter is nonzero then the whole process is repeated. Otherwise, the loop is terminated. The do loop is less frequently used than the while loop. It is useful for situations where we need the loop body to be executed at least once, regardless of the loop condition. For example, suppose 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. 43 Department of CS Fundamental of Programming - I 3.4. Other Statements 3.4.1. The ‘continue’ Statement The continue statement terminates the current iteration of a loop and instead jumps to the next iteration. It applies to the loop immediately enclosing the continue statement. It is an error to use the continue statement outside a loop. In while and do loops, the next iteration commences from the loop condition. In a for loop, the next iteration commences from the loop‘s third expression. For example, a loop which repeatedly reads in a number, processes it but ignores negative numbers, and terminates when the number is zero, may be expressed as: do { cin >> num; if (num < 0) continue; // process num here... } while (num != 0); This is equivalent to: do { cin >> num; if (num >= 0) { // process num here... } } while (num != 0); A variant of this loop which reads in a number exactly n times (rather than until the number is zero) may be expressed as: for (i = 0; i < n; ++i) { cin >> num; if (num < 0) continue; // 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... } 44 // causes a jump to: ++i Department of CS Fundamental of Programming - 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. 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 45 Department of CS Fundamental of Programming - I cout << "Incorrect!\n"; } out: //etc... Because goto provides a free and unstructured form of jumping (unlike break and continue), it can be easily misused. Most programmers these days avoid using it altogether in favor of clear programming. Nevertheless, goto does have some legitimate (though rare) uses. 3.4.4. The ‘return’ Statement The return statement enables a function to return a value to its caller. It has the general form: return expression; where expression denotes the value returned by the function. The type of this value should match the return type of the function. For a function whose return type is void, expression should be empty: return; The only function we have discussed so far is main, whose return type is always int. The return value of main is what the program returns to the operating system when it completes its execution. Under UNIX, for example, it its conventional to return 0 from main when the program executes without errors. Otherwise, a non-zero error code is returned. For example: int main (void) { cout << "Hello World\n"; return 0; } When a function has a non-void return value (as in the above example), failing to return a value will result in a compiler warning. The actual return value will be undefined in this case (i.e., it will be whatever value which happens to be in its corresponding memory location at the time). 46