Academia.eduAcademia.edu
This page intentionally left blank C++ for EvEryonE cfe2_fm_pi_xxvi.indd 1 10/28/10 5:08 PM This page intentionally left blank C++ for EvEryonE Second edition cay Horstmann San Jose State University John Wiley & Sons, Inc. cfe2_fm_pi_xxvi.indd 3 10/28/10 5:08 PM VICE PRESIDENT AND EXECUTIVE PUBLISHER EXECUTIVE EDITOR EDITORIAL PROGRAM ASSISTANT PRODUCTION SERVICES MANAGER SENIOR PRODUCTION EDITOR EXECUTIVE MARKETING MANAGER MARKETING ASSISTANT CREATIVE DIRECTOR SENIOR PHOTO EDITOR SENIOR DESIGNER EXECUTIVE MEDIA EDITOR PRODUCTION SERVICES COVER PHOTO Don Fowley Beth Lang Golub Michael Berlin Dorothy Sinclair Janet Foxman Christopher Ruel Diana Smith Harry Nolan Lisa Gee Madelyn Lesure Tom Kulesa Cindy Johnson © Ricardo Azoury/iStockphoto This book was set in Stempel Garamond by Publishing Services, and printed and bound by RRD Jefferson City. The cover was printed by RRD Jefferson City. This book is printed on acid-free paper. ∞ Founded in 1807, John Wiley & Sons, Inc. has been a valued source of knowledge and understanding for more than 200 years, helping people around the world meet their needs and fulfill their aspirations. Our company is built on a foundation of principles that include responsibility to the communities we serve and where we live and work. In 2008, we launched a Corporate Citizenship Initiative, a global effort to address the environmental, social, economic, and ethical challenges we face in our business. Among the issues we are addressing are carbon impact, paper specifications and procurement, ethical conduct within our business and among our vendors, and community and charitable support. For more information, please visit our website: www.wiley.com/go/ citizenship. Copyright © 2012, 2009 John Wiley & Sons, Inc. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, scanning or otherwise, except as permitted under Sections 107 or 108 of the 1976 United States Copyright Act, without either the prior written permission of the Publisher, or authorization through payment of the appropriate per-copy fee to the Copyright Clearance Center, Inc., 222 Rosewood Drive, Danvers, MA 01923, website www.copyright.com. Requests to the Publisher for permission should be addressed to the Permissions Department, John Wiley & Sons, Inc., 111 River Street, Hoboken, NJ 07030-5774, (201) 7486011, fax (201) 748-6008, website www.wiley.com/go/permissions. Evaluation copies are provided to qualified academics and professionals for review purposes only, for use in their courses during the next academic year. These copies are licensed and may not be sold or transferred to a third party. Upon completion of the review period, please return the evaluation copy to Wiley. Return instructions and a free of charge return shipping label are available at www.wiley.com/go/returnlabel. Outside of the United States, please contact your local representative. Library of Congress Cataloging in Publication Data: Horstmann, Cay S., 1959C++ for everyone / Cay S. Horstmann. -- 2nd ed. p. cm. Includes index. ISBN 978-0-470-92713-7 (pbk.) 1. C++ (Computer program language) I. Title. QA76.73.C153H6685 2010 005.13'3--dc22 2010039907 ISBN 978-0-470-92713-7 (Main Book) ISBN 978-0-470-92092-3 (Binder-Ready Version) Printed in the United States of America 10 9 8 7 6 5 4 3 2 1 cfe2_fm_pi_xxvi.indd 4 10/28/10 5:08 PM P r E fa C E This book is an introduction to C++ and computer programming that focuses on the essentials—and on effective learning. The book is designed to serve a wide range of student interests and abilities and is suitable for a first course in programming for computer scientists, engineers, and students in other disciplines. No prior programming experience is required, and only a modest amount of high school algebra is needed. Here are the key features of this book: Guidance and worked examples help students succeed. Beginning programmers often ask “How do I start? Now what do I do?” Of course, an activity as complex as programming cannot be reduced to cookbook-style instructions. However, step-by-step guidance is immensely helpful for building confidence and providing an outline for the task at hand. “Problem Solving” sections stress the importance of design and planning. “How To” guides help students with common programming tasks. Additional Worked Examples are available online. Practice makes perfect. Of course, programming students need to be able to implement nontrivial programs, but they first need to have the confidence that they can succeed. This book contains a substantial number of self-check questions at the end of each section. “Practice It” pointers suggest exercises to try after each section. At the end of each chapter, you will find a great variety of programming assignments, ranging from simple practice problems to realistic applications. teach computer science principles, not just c++ or object-orientation. This book uses the C++ programming language as a vehicle for introducing computer science concepts. A substantial subset of the C++ language is covered, focusing on the modern features of standard C++ that make students productive. The book takes a traditional route, stressing control structures, procedural decomposition, and array algorithms, before turning to the design of classes in the final chapters. A visual approach motivates the reader and eases navigation. Photographs present visual analogies that explain the nature and behavior of computer concepts. Step-bystep figures illustrate complex program operations. Syntax boxes and example tables present a variety of typical and special cases in a compact format. It is easy to get the “lay of the land” by browsing the visuals, before focusing on the textual material. Focus on the essentials while being technically accurate. Visual features help the reader An encyclopedic coverage is not helpful for a begin- with navigation. ning programmer, but neither is the opposite— reducing the material to a list of simplistic bullet points. In this book, the essentials are presented in digestible chunks, with separate notes that go deeper into good practices or language features when the reader is ready for the additional information. You will not find artificial over-simplifications that give an illusion of knowledge. v cfe2_fm_pi_xxvi.indd 5 10/28/10 5:08 PM vi Preface new to This Edition Problem Solving Strategies This edition adds practical, step-by-step illustrations of techniques that can help students devise and evaluate solutions to programming problems. Introduced where they are most relevant, these strategies address barriers to success for many students. Strategies included are: • Algorithm Design (with pseudocode) • First Do It By Hand (doing sample calculations by hand) • Flowcharts • Test Cases • Hand-Tracing • Storyboards • Reusable Functions • Stepwise Refinement • Adapting Algorithms • Discover Algorithms by Manipulating Physical Objects • Draw a Picture (pointer diagrams) • Tracing Objects (identifying state and behavior) • Discovering Classes optional Engineering Exercises End-of-chapter exercises have been enhanced with problems from scientific and engineering domains. Geared to students learning C++ for a technical major, the exercises are designed to illustrate the value of programming in those fields. Additional exercises are available on the book’s companion web site. new and reorganized Topics All chapters were revised and enhanced to respond to user feedback and improve the flow of topics. Loop algorithms are now introduced explicitly in Chapter 4. Debugging is now introduced in a lengthy Worked Example in Chapter 5. Arrays are covered before vectors are introduced in Chapter 6, and a new section on vector algorithms builds on the array algorithms presented earlier in the chapter. A new optional section on structure types is now in Chapter 7. New example tables, photos, and exercises appear throughout the book. a Tour of the Book The core material of the book is: Chapter 1. Introduction Chapter 2. Fundamental Data Types Chapter 3. Decisions Chapter 4. Loops Chapter 5. Functions Chapter 6. Arrays and Vectors In a course for engineers with a need for systems and embedded programming, you will want to cover Chapter 7 on pointers. Sections 7.1 and 7.4 are sufficient for using pointers with polymorphism in Chapter 10. cfe2_fm_pi_xxvi.indd 6 10/28/10 5:08 PM Preface vii File processing is the subject of Chapter 8. Section 8.1 can be covered sooner for an introduction to reading and writing text files. The remainder of the chapter gives additional material for practical applications. Chapters 9 and 10 introduce the object-oriented features of C++. Chapter 9 introduces class design and implementation. Chapter 10 covers inheritance and polymorphism. Four additional chapters are available on the Web. They can be used individually for a capstone chapter, or they can be combined for teaching a two-semester course. (They can also be incorporated into a custom print version of the text; ask your Wiley sales representative for details.) Chapter 11. Recursion Chapter 12. Sorting and Searching Chapter 13. Lists, Stacks, and Queues Chapter 14. Sets, Maps, and Priority Queues Figure 1 shows the dependencies between the chapters. Fundamentals 1. Introduction Online 2. Fundamental Data Types 3. Decisions 4. Loops 5. Functions Section 8.1 contains the core material Figure 1 Chapter Dependencies cfe2_fm_pi_xxvi.indd 7 A gentle introduction to recursion is optional 6. Arrays and Vectors 7. Pointers 8. Streams 9. Classes Sections 7.1 and 7.4 are required 13. Lists, Stacks, and Queues 10. Inheritance 11. Recursion 12. Sorting and Searching 14. Sets, Maps, Priority Queues 10/28/10 5:08 PM viii Walkthrough a Walkthrough of the Learning aids The pedagogical elements in this book work together to focus on and reinforce key concepts and fundamental principles of programming, with additional tips and detail organized to support and deepen these fundamentals. In addition to traditional features, such as chapter objectives and a wealth of exercises, each chapter contains elements geared to today’s visual learner. 4.3 The for Loop Throughout each chapter, margin notes show where new concepts are introduced and provide an outline of key ideas. 143 4.4 The for Loop The for loop is used when a value runs from a starting point to an ending point with a constant increment or decrement. It often happens that you want to execute a sequence of statements a given number of times. You can use a while loop that is controlled by a counter, as in the following example: counter = 1; // Initialize the counter while (counter <= 10) // Check the counter { cout << counter << endl; counter++; // Update the counter } Because this loop type is so common, there is a special form for it, called the for loop (see Syntax 4.2). for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } Some people call this loop count-controlled. In contrast, the while loop of the preceding section can be called an event-controlled loop because it executes until an event occurs (for example, when the balance reaches the target). Another commonly-used term for a count-controlled loop is definite. You know from the outset that the loop body will be executed a definite number of times––ten times in our example. In contrast, you do not know how many iterations it takes to accumulate a target balance. Such a loop is called indefinite. The for loop neatly groups the initialization, condition, and update expressions together. However, it is important to realize that these expressions are not executed together (see Figure 3). Annotated syntax boxes provide a quick, visual overview of new language constructs. Syntax 4.2 You can visualize the for loop as an orderly sequence of steps. for Statement These three expressions should be related. See page 147. Annotations explain required components and point to more information on common errors or best practices associated with the syntax. Like a variable in a computer program, a parking space has an identifier and a contents. cfe2_fm_pi_xxvi.indd 8 This initialization happens once before the loop starts. The variable i is defined only in this for loop. See page 144. The loop is executed while this condition is true. for (int i = 5; i <= 10; i++) { sum = sum + i; } This update is executed after each iteration. This loop executes 6 times. See page 147. Analogies to everyday objects are used to explain the nature and behavior of concepts such as variables, data types, loops, and more. 10/28/10 5:08 PM Walkthrough ix Memorable photos reinforce analogies and help students remember the concepts. pie(fruit) pie(fruit) A recipe for a fruit pie may say to use any kind of fruit. Here, “fruit” is an example of a parameter variable. Apples and cherries are examples of arguments. Problem Solving sections teach techniques for generating ideas and evaluating proposed solutions, often using pencil and paper or other artifacts. These sections emphasize that most of the planning and problem solving that makes students successful happens away from the computer. 6.5 Problem Solving: Discovering Algorithms by Manipulating Physical Objects 277 Now how does that help us with our problem, switching the first and the second half of the array? Let’s put the first coin into place, by swapping it with the fifth coin. However, as C++ programmers, we will say that we swap the coins in positions 0 and 4: Next, we swap the coins in positions 1 and 5: HOW TO 1.1 Describing an Algorithm with Pseudocode Before you are ready to write a program in C++, you need to develop an algorithm—a method for arriving at a solution for a particular problem. Describe the algorithm in pseudocode: a sequence of precise steps formulated in English. For example, consider this problem: You have the choice of buying two cars. One is more fuel efficient than the other, but also more expensive. You know the price and fuel efficiency (in miles per gallon, mpg) of both cars. You plan to keep the car for ten years. Assume a price of $4 per gallon of gas and usage of 15,000 miles per year. You will pay cash for the car and not worry about financing costs. Which car is the better deal? Step 1 How To guides give step-by-step guidance for common programming tasks, emphasizing planning and testing. They answer the beginner’s question, “Now what do I do?” and integrate key concepts into a problem-solving sequence. Determine the inputs and outputs. In our sample problem, we have these inputs: • purchase price1 and fuel efficiency1, the price and fuel efficiency (in mpg) of the first car • purchase price2 and fuel efficiency2, the price and fuel efficiency of the second car We simply want to know which car is the better buy. That is the desired output. WORKED EXAMPLE 1.1 Writing an Algorithm for Tiling a Floor This Worked Example shows how to develop an algorithm for laying tile in an alternating pattern of colors. Worked Examples apply the steps in the How To to a different example, showing how they can be used to plan, implement, and test a solution to another programming problem. Table 3 Variable Names in C++ Variable Name ! cfe2_fm_pi_xxvi.indd 9 Comment can_volume1 Variable names consist of letters, numbers, and the underscore character. x In mathematics, you use short variable names such as x or y. This is legal in C++, but not very common, because it can make programs harder to understand (see Programming Tip 2.1 on page 38). Can_volume Caution: Variable names are case-sensitive. This variable name is different from can_volume. 6pack Error: Variable names cannot start with a number. can volume Error: Variable names cannot contain spaces. double Error: You cannot use a reserved word as a variable name. ltr/fl.oz Error: You cannot use symbols such as / or. Example tables support beginners with multiple, concrete examples. These tables point out common errors and present another quick reference to the section’s topic. 10/28/10 5:08 PM x Walkthrough Figure 3 Execution of a 1 Initialize counter 1 for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } 1 for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } for Loop counter = 2 Check condition counter = 3 Execute loop body counter = 1 for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } 2 for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } Progressive figures trace code segments to help students visualize the program flow. Color is used consistently to make variables and other elements easily recognizable. 1 Function call result1 = double result1 = cube_volume(2); 4 Update counter counter = 5 Check condition again counter = 2 side_length = 2 result1 = double result1 = cube_volume(2); for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } side_length = 3 Self-check exercises at the end of each section are designed to make students think through the new material—and can spark discussion in lecture. SELF CHECK Initializing function parameter variable 11. 12. About to return to the caller double volume = side_length * side_length * side_length; return volume; 4 After function call 2 result1 = side_length = 2 volume = 8 result1 = 8 double result1 = cube_volume(2); Figure 3 Parameter Passing • The parameter variable side_length of the cube_volume function is created. 1 • The parameter variable is initialized with the value of the argument that was passed in the call. In our case, side_length is set to 2. 2 • The function computes the expression side_length * side_length * side_length, which has the value 8. That value is stored in the variable volume. 3 Write the for loop of the invtable.cpp program as a while loop. • The function returns. All of its variables are removed. The return value is transHow many numbers does this loop print? ferred to the caller, that is, the function calling the cube_volume function. 4 for (int n = 10; n >= 0; n--) { cout << n << endl; } 13. 14. 15. Practice It Write a for loop that prints all even numbers between 10 and 20 (inclusive). Write a for loop that computes the sum of the integers from 1 to n. How would you modify the for loop of the invtable.cpp program to print all balances until the investment has doubled? Now you can try these exercises at the end of the chapter: R4.2, R4.7, P4.12. Optional engineering exercises engage students with applications from technical fields. ch05/cube.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> using namespace std; /** Engineering P7.20 Write a program that simulates the control software for a “people mover” system, a set of driverless trains that move in two concentric circular tracks. A set of switches allows trains to switch tracks. In your program, the outer and inner tracks should each be divided into ten segments. Each track segment can contain a train that moves either clockwise or counterclockwise. A train moves to an adjacent segment in its track or, if that segment is occupied, to the adjacent segment in the other track. Define a Segment structure. Each segment has a pointer to the next and previous segments in its track, a pointer to the next and previous segments in the other track, Computes the volume of a cube. @param side_length the side length of the cube @return the volume */ double cube_volume(double side_length) { double volume = side_length * side_length * side_length; return volume; } int main() { double result1 = cube_volume(2); double result2 = cube_volume(10); cout << "A cube with side length 2 has volume " << result1 << endl; cout << "A cube with side length 10 has volume " << result2 << endl; return 0; } Program listings are carefully designed for easy reading, going well beyond simple color coding. Functions are set off by a subtle outline. Program Run A cube with side length 2 has volume 8 A cube with side length 10 has volume 1000 cfe2_fm_pi_xxvi.indd 10 10/28/10 5:08 PM Walkthrough xi Common Error 2.1 Common Errors describe the kinds of errors that students often make, with an explanation of why the errors occur, and what to do about them. Programming Tip 3.6 Programming Tips explain good programming practices, and encourage students to be more productive with tips and techniques such as hand-tracing. Using Undefined Variables You must define a variable before you use it for the first time. For example, the following sequence of statements would not be legal: double can_volume = 12 * liter_per_ounce; double liter_per_ounce = 0.0296; In your program, the statements are compiled in order. When the compiler reaches the first statement, it does not know that liter_per_ounce will be defined in the next line, and it reports an error. Hand-Tracing A very useful technique for understanding whether a program works correctly is called hand-tracing. You simulate the program’s activity on a sheet of paper. You can use this method with pseudocode or C++ code. Get an index card, a cocktail napkin, or whatever sheet of paper is within reach. Make a column for each variable. Have the program code ready. Use a marker, such as a paper clip, to mark the current statement. In your mind, execute statements one at a time. Every time the value of a variable changes, cross out the old value and write the new value below the old one. For example, let’s trace the tax program with the data from the program run on page 95. In lines 13 and 14, tax1 and tax2 are initialized to 0. 6 int main() 7 { 8 const double RATE1 = 0.10; 9 const double RATE2 = 0.25; 10 const double RATE1_SINGLE_LIMIT = 32000; 11 const double RATE1_MARRIED_LIMIT = 64000; 12 double tax1 = 0; 13 14 double tax2 = 0; 15 Hand-tracing helps you understand whether a program works correctly. tax1 tax2 0 0 income marital status In lines 18 and 22, income and marital_status are initialized by input statements. 16 17 18 19 20 21 22 23 double income; cout << "Please enter your income: "; cin >> income; cout << "Please enter s for single, m for married: "; string marital_status; cin >> marital_status; tax1 tax2 income marital status 0 0 80000 m Because marital_status is not "s", we move to the else branch of the outer if statement (line 36). Special Topic 6.2 A Sorting Algorithm A sorting algorithm rearranges the elements of a sequence so that they are stored in sorted order. Here is a simple sorting algorithm, called selection sort. Consider sorting the following array values: [0] [1] [2] [3] [4] 11 9 17 5 12 Special Topics present optional topics and provide additional explanation of others. An obvious first step is to find the smallest element. In this case the smallest element is 5, stored in values[3]. You should move the 5 to the beginning of the array. Of course, there is already an element stored in values[0], namely 11. Therefore you cannot simply move values[3] into values[0] without moving the 11 somewhere else. You don’t yet know where the 11 should end up, but you know for certain that it should not be in values[0]. Simply get it out of the way by swapping it with values[3]: 5 9 17 11 12 Now the first element is in the correct place. In the foregoing figure, the darker color indicates the portion of the array that is already sorted. Next take the minimum of the remaining entries values[1]...values[4]. That minimum value, 9, is already in the correct place. You don’t need to do anything in this case, simply extend the sorted area by one to the right: 5 9 17 11 12 Repeat the process. The minimum value of the unsorted region is 11, which needs to be swapped with the first value of the unsorted region, 17: 5 Random Fact 4.1 Random Facts provide historical and social information on computing—for interest and to fulfill the “historical and social context” requirements of the ACM/IEEE curriculum guidelines. cfe2_fm_pi_xxvi.indd 11 According to legend, the first bug was found in the Mark II, a huge electromechanical computer at Harvard University. It really was caused by a bug—a moth was trapped in a relay switch. Actually, from the note that the operator left in the log book next to the moth (see the photo), it appears as if the term “bug” had already been in active use at the time. 9 11 17 12 The First Bug The pioneering computer scientist Maurice Wilkes wrote, “Somehow, at the Moore School and afterwards, one had always assumed there would be no particular difficulty in getting pro- grams right. I can remember the exact instant in time at which it dawned on me that a great part of my future life would be spent finding mistakes in my own programs.” The First Bug 10/28/10 5:09 PM xii Preface appendices Appendix A contains a programming style guide. Using a style guide for programming assignments benefits students by directing them toward good habits and reducing gratuitous choice. The style guide is available in electronic form so that instructors can modify it to reflect their preferred style. Appendices B and C summarize C++ reserved words and operators. Appendix D lists character escape sequences and ASCII character code values. Appendix E documents all of the library functions and classes used in this book. Additional appendices available from the book’s companion web site include an expanded version of Appendix E that includes the functions and classes used in the four optional chapters, 11–14, plus appendices that cover number systems, bit and shift operations, and a comparison of C++ and Java. Student and Instructor resources The following resources for students and instructors can be obtained by visiting www.wiley.com/college/horstmann. Two companion web sites accompany the book— one for students, and a password-protected site for instructors only. • Additional exercises geared to the scientific and engineering problem domains • Worked Examples that apply the problem-solving steps in the book to other realistic examples (identified in the book by an icon, ) • Source code for all examples in the book • Solutions to all review and programming exercises (for instructors only) • Lecture presentation slides (in PowerPoint format) that summarize each chapter and include code listings and figures from the book (for instructors only) • A test bank that focuses on skills, not just terminology (for instructors only) • Four additional chapters on recursion, sorting and searching, and data structures • The programming style guide in electronic form Pointers in the book describe what students will find on the Web. WORKED EXAMPLE 2.1 Computing Travel Time In this Worked Example, we develop a hand calculation to compute the time that a robot requires to retrieve an item from rocky terrain. Visit the C++ for Everyone companion web sites at www.wiley.com/college/horstmann. cfe2_fm_pi_xxvi.indd 12 10/28/10 5:09 PM acknowledgments xiii acknowledgments Many thanks to Beth Golub, Tom Kulesa, Andre Legaspi, Elizabeth Mills, Michael Berlin, and Lisa Gee at John Wiley & Sons, and to the team at Publishing Services for their hard work and support for this book project. An especially deep acknowledgment and thanks to Cindy Johnson, who, through enormous patience and attention to detail, made this book a reality. We would also like to thank Jonathan Tolstedt, North Dakota State University, for his high-quality solutions; Brent Seales, University of Kentucky, for revising and enhancing the test bank; and to Evan Gallagher, Polytechnic Institute of New York University, for his creative PowerPoint slides. We are very grateful to the many individuals who reviewed and/or class tested this and the first edition of the book. We value their many valuable suggestions for improvement. They include: Charles D. Allison, Utah Valley State College Fred Annexstein, University of Cincinnati Stefano Basagni, Northeastern University Noah D. Barnette, Virginia Tech Susan Bickford, Tallahassee Community College Ronald D. Bowman, University of Alabama, Huntsville Peter Breznay, University of Wisconsin, Green Bay Richard Cacace, Pensacola Junior College, Pensacola Kuang-Nan Chang, Eastern Kentucky University Joseph DeLibero, Arizona State University Subramaniam Dharmarajan, Arizona State University Mary Dorf, University of Michigan Marty Dulberg, North Carolina State University William E. Duncan, Louisiana State University John Estell, Ohio Northern University Waleed Farag, Indiana University of Pennsylvania Stephen Gilbert, Orange Coast Community College Kenneth Gitlitz, New Hampshire Technical Institute Daniel Grigoletti, DeVry Institute of Technology, Tinley Park Barbara Guillott, Louisiana State University Charles Halsey, Richland College Jon Hanrath, Illinois Institute of Technology Neil Harrison, Utah Valley University Jurgen Hecht, University of Ontario Steve Hodges, Cabrillo College Jackie Jarboe, Boise State University Debbie Kaneko, Old Dominion University Mir Behrad Khamesee, University of Waterloo Sung-Sik Kwon, North Carolina Central University cfe2_fm_pi_xxvi.indd 13 10/28/10 5:09 PM xiv acknowledgments Lorrie Lehman, University of North Carolina, Charlotte Cynthia Lester, Tuskegee University Yanjun Li, Fordham University W. James MacLean, University of Toronto LindaLee Massoud, Mott Community College Charles W. Mellard, DeVry Institute of Technology, Irving Ethan V. Munson, University of Wisconsin, Milwaukee Philip Regalbuto, Trident Technical College Don Retzlaff, University of North Texas Jeff Ringenberg, University of Michigan, Ann Arbor John P. Russo, Wentworth Institute of Technology Kurt Schmidt, Drexel University Brent Seales, University of Kentucky William Shay, University of Wisconsin, Green Bay Michele A. Starkey, Mount Saint Mary College William Stockwell, University of Central Oklahoma Jonathan Tolstedt, North Dakota State University Boyd Trolinger, Butte College Muharrem Uyar, City College of New York Mahendra Velauthapillai, Georgetown University Kerstin Voigt, California State University, San Bernardino David P. Voorhees, Le Moyne College Salih Yurttas, Texas A&M University A special thank you to all of our class testers: Pani Chakrapani and the students of the University of Redlands Jim Mackowiak and the students of Long Beach City College, LAC Suresh Muknahallipatna and the students of the University of Wyoming Murlidharan Nair and the students of the Indiana University of South Bend Harriette Roadman and the students of New River Community College David Topham and the students of Ohlone College Dennie Van Tassel and the students of Gavilan College cfe2_fm_pi_xxvi.indd 14 10/28/10 5:09 PM ConTEnTS PrEfaCE v SPECIaL fEaTurES xx cHAPter 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 52 DECISIonS 75 The if Statement 76 Comparing numbers and Strings 82 Multiple alternatives 90 nested Branches 94 Problem Solving: flowcharts 99 Problem Solving: Test Cases 102 Boolean variables and operators 103 application: Input validation 109 cHAPter 4 4.1 4.2 4.3 4.4 4.5 7 funDaMEnTaL DaTa TyPES 29 variables 30 arithmetic 40 Input and output 48 Problem Solving: first Do It By Hand Strings 56 cHAPter 3 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 1 What is Programming 2 The anatomy of a Computer 3 Machine Code and Programming Languages 6 Becoming familiar with your Programming Environment analyzing your first Program 11 Errors 15 Problem Solving: algorithm Design 17 cHAPter 2 2.1 2.2 2.3 2.4 2.5 InTroDuCTIon LooPS 131 The while Loop 132 Problem Solving: Hand-Tracing The for Loop 142 The do Loop 148 Processing Input 150 139 xv cfe2_fm_pi_xxvi.indd 15 10/28/10 5:09 PM xvi Contents 4.6 4.7 4.8 4.9 Problem Solving: Storyboards 154 Common Loop algorithms 157 nested Loops 165 random numbers and Simulations 168 cHAPter 5 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.10 6.6 6.7 cfe2_fm_pi_xxvi.indd 16 PoInTErS 307 Defining and using Pointers 308 arrays and Pointers 314 C and C++ Strings 320 Dynamic Memory allocation 325 arrays and vectors of Pointers 329 Problem Solving: Draw a Picture 332 Structures and Pointers (optional) 336 cHAPter 8 8.1 8.2 arrayS anD vECTorS 249 arrays 250 Common array algorithms 256 arrays and functions 265 Problem Solving: adapting algorithms 269 Problem Solving: Discovering algorithms by Manipulating Physical objects 274 Two-Dimensional arrays 278 vectors 284 cHAPter 7 7.1 7.2 7.3 7.4 7.5 7.6 7.7 193 functions as Black Boxes 194 Implementing functions 196 Parameter Passing 199 return values 202 functions Without return values 206 Problem Solving: reusable functions 208 Problem Solving: Stepwise refinement 210 variable Scope and Global variables 218 reference Parameters 220 recursive functions (optional) 226 cHAPter 6 6.1 6.2 6.3 6.4 6.5 funCTIonS STrEaMS 351 reading and Writing Text files reading Text Input 358 352 10/28/10 5:09 PM Contents xvii 8.3 8.4 8.5 8.6 Writing Text output 361 String Streams 363 Command Line arguments 365 random access and Binary files 372 cHAPter 9 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9 CLaSSES 389 object-oriented Programming 390 Specifying the Public Interface of a Class 392 Data Members 395 Member functions 397 Constructors 403 Problem Solving: Tracing objects 407 Problem Solving: Discovering Classes 414 Separate Compilation 417 Pointers to objects 422 cHAPter 10 10.1 10.2 10.3 10.4 Inheritance Hierarchies 442 Implementing Derived Classes 446 overriding Member functions 451 virtual functions and Polymorphism 455 cHAPter 11 11.1 11.2 11.3 11.4 11.5 11.6 rECurSIon (WEB onLy) Triangle numbers Thinking recursively recursive Helper functions The Efficiency of recursion Permutations Mutual recursion cHAPter 12 12.1 12.2 12.3 12.4 12.5 12.6 InHErITanCE 441 SorTInG anD SEarCHInG (WEB onLy) Selection Sort Profiling the Selection Sort algorithm analyzing the Performance of the Selection Sort algorithm Merge Sort analyzing the Merge Sort algorithm Searching Available online at www.wiley.com/college/horstmann. cfe2_fm_pi_xxvi.indd 17 10/28/10 5:09 PM xviii Contents cHAPter 13 13.1 13.2 13.3 13.4 (WEB onLy) using Linked Lists Implementing Linked Lists The Efficiency of List, array, and vector operations Stacks and Queues cHAPter 14 14.1 14.2 14.3 14.4 14.5 LISTS, STaCkS, anD QuEuES SETS, MaPS, anD PrIorITy QuEuES (WEB onLy) Sets Binary Search Trees Maps Priority Queues Heaps APPendiceS aPPEnDIx a aPPEnDIx B aPPEnDIx C aPPEnDIx D aPPEnDIx E aPPEnDIx f C++ LanGuaGE CoDInG GuIDELInES rESErvED WorD SuMMary 489 oPEraTor SuMMary 491 CHaraCTEr CoDES 493 C++ LIBrary SuMMary 495 nuMBEr SySTEMS (WEB onLy) aPPEnDIx G aPPEnDIx H BIT anD SHIfT oPEraTIonS a C++ / Java CoMParISon 481 (WEB onLy) (WEB onLy) GLoSSary 499 InDEx 507 CrEDITS 529 Available online at www.wiley.com/college/horstmann. cfe2_fm_pi_xxvi.indd 18 10/28/10 5:09 PM Contents xix SynTax BoxES AlPHAbeticAl liSt oF assignment 34 C++ Program 12 Class Definition 393 Comparisons 83 Constructor with Base-Class Initializer 451 Defining an array 251 Defining a vector 285 Derived-Class Definition 448 Dynamic Memory allocation 325 for Statement 144 function Definition 197 if Statement 78 Input Statement 48 Member function Definition output Statement Pointer Syntax 400 13 310 Two-Dimensional array Definition variable Definition 31 while Statement 133 Working with file Streams cfe2_fm_pi_xxvi.indd 19 279 354 10/28/10 5:09 PM xx Special features CH aP TE r How Tos Common Errors and Worked Examples 1 Introduction omitting Semicolons Misspelling Words 14 16 Describing an algorithm with Pseudocode 20 Writing an algorithm for Tiling a floor 2 fundamental using undefined variables using uninitialized variables unintended Integer Division unbalanced Parentheses forgetting Header files roundoff Errors 37 37 43 44 45 45 Computing Travel Time Carrying out Computations Computing the Cost of Stamps Data Types 3 Decisions a Semicolon after the if Condition 80 Confusing = and == 85 Exact Comparison of floatingPoint numbers 86 The Dangling else Problem 98 Combining Multiple relational operators 107 Confusing && and || Conditions 107 Implementing an if Statement Extracting the Middle 4 Loops Infinite Loops Don’t Think “are We There yet?” off-by-one Errors 136 Writing a Loop Credit Card Processing Missing return value 203 5 functions 54 87 162 137 137 Implementing a function 204 Matching and replacing Parts of a String using a Debugger Calculating a Course Grade Thinking recursively 229 Available online at www.wiley.com/college/horstmann. cfe2_fm_pi_xxvi.indd 20 10/28/10 5:09 PM Special Features xxi Programming Tips Backup Copies 11 Choose Descriptive Variable Names Do Not Use Magic Numbers Spaces in Expressions 38 39 46 Special Topics Escape Sequences Numeric Types in C++ Numeric Ranges and Precisions Casts Combining Assignment and Arithmetic 14 38 39 46 Random Facts The ENIAC and the Dawn of Computing Standards Organizations The Pentium Floating-Point Bug International Alphabets and Unicode 5 7 47 61 47 Brace Layout 79 Always Use Braces 80 Tabs 81 Avoid Duplication in Branches 82 Compile With Zero Warnings 85 Hand-Tracing 97 Make a Schedule and Make Time for Unexpected Problems 103 The Selection Operator Lexicographic Ordering of Strings The switch Statement Short-Circuit Evaluation of Boolean Operators De Morgan’s Law Use for Loops for Their Intended Purpose Only Choose Loop Bounds That Match Your Task Count Iterations Flowcharts for Loops Clearing the Failure State 153 The Loop-and-a-Half Problem and the break Statement 153 Redirection of Input and Output 154 The First Bug Software Piracy 138 172 Function Declarations Constant References The Explosive Growth of Personal Computers 230 Function Comments Don’t Modify Parameter Variables Keep Functions Short Tracing Functions Stubs Avoid Global Variables Prefer Return Values to Reference Parameters 147 147 147 149 199 81 86 93 The Denver Airport Luggage Handling System 89 Artificial Intelligence 112 108 108 203 225 201 216 216 217 220 225 Available online at www.wiley.com/college/horstmann. cfe2_fm_pi_xxvi.indd 21 11/9/10 9:55 AM xxii Special features CH aP TE r 6 arrays and vectors 7 Pointers How Tos Common Errors Bounds Errors omitting the Column Size of a TwoDimensional array Parameter Confusing Pointers with the Data to Which They Point returning a Pointer to a Local variable Dangling Pointers Memory Leaks and Worked Examples 254 Working with arrays rolling the Dice a World Population Table 271 Working with Pointers Producing a Mass Mailing 334 Processing Text files Looking for for Duplicates 369 409 284 313 319 328 328 8 Streams 9 Classes forgetting a Semicolon 395 Trying to Call a Constructor 405 Implementing a Class Implementing a Bank account Class 10 Inheritance Private Inheritance replicating Base-Class Members forgetting the BaseClass name Slicing an object Developing an Inheritance Hierarchy Implementing an Employee Hierarchy for Payroll Processing 449 450 455 462 464 Available online at www.wiley.com/college/horstmann. cfe2_fm_pi_xxvi.indd 22 10/28/10 5:09 PM Special Features xxiii Programming Tips Use Arrays for Sequences of Related Values Prefer Vectors over Arrays 255 289 Use a Separate Definition for Each Pointer Variable 313 Program Clearly, Not Cleverly 319 All Data Members Should Be Private; Most Member Functions Should Be Public const Correctness Make Parallel Vectors into Vectors of Objects 402 402 Special Topics Random Facts Sorting with the C++ Library A Sorting Algorithm Binary Search Constant Array Parameters 263 263 264 269 An Early Internet Worm The First Programmer 255 290 Pointers and References Using a Pointer to Step Through an Array Constant Pointers Working with C Strings 314 Embedded Systems 336 Encryption Algorithms Databases and Privacy 368 377 Electronic Voting Machines Open Source and Free Software 412 The Limits of Computation 469 318 320 323 Initializer Lists Overloading Destructors and Resource Management 405 406 424 Calling the Base-Class Constructor Virtual Self-Calls 451 463 426 416 Use a Single Class for Variation in Values, Inheritance for Variation in Behavior 450 Don’t Use Type Tags 462 Available online at www.wiley.com/college/horstmann. cfe2_fm_pi_xxvi.indd 23 11/9/10 9:55 AM xxiv Special features CH aP TE r 11 recursion (WEB onLy) Common Errors How Tos and Worked Examples Infinite recursion Tracing Through recursive functions 12 Sorting and Searching (WEB onLy) 13 Lists, Stacks, and Queues (WEB onLy) 14 Sets, Maps, and Priority Queues (WEB onLy) Available online at www.wiley.com/college/horstmann. cfe2_fm_pi_xxvi.indd 24 10/28/10 5:09 PM Special Features xxv Programming Tips Library Functions for Sorting and Binary Search Special Topics The Quicksort Algorithm Defining an Ordering for Sorting Objects Random Facts Cataloging Your Necktie Collection Reverse Polish Notation Defining an Ordering for Container Elements Constant Iterators Discrete Event Simulations Available online at www.wiley.com/college/horstmann. cfe2_fm_pi_xxvi.indd 25 11/9/10 9:56 AM This page intentionally left blank Chapter 1 IntroduCtIon Chapter Goals to learn about the architecture of computers to learn about machine languages and higher-level programming languages to become familiar with your compiler to compile and run your first C++ program to recognize compile-time and run-time errors to describe an algorithm with pseudocode to understand the activity of programming Chapter Contents 2 1.1 What Is ProgrammIng? 1.2 the anatomy of a ComPuter 3 Common Error 1.1: omitting semicolons 14 Special Topic 1.1: escape sequences 14 1.6 1.3 maChIne Code and ProgrammIng Languages 6 1.7 ProBLem soLvIng: aLgorIthm desIgn 17 Random Fact 1.2: standards organizations 7 How To 1.1: describing an algorithm with pseudocode 20 Worked Example 1.1: Writing an algorithm for tiling a Floor 1.4 BeComIng famILIar WIth your ProgrammIng envIronment 7 Programming Tip 1.1: Backup Copies 11 errors 15 Random Fact 1.1: the enIaC and the dawn of Computing 5 Common Error 1.2: Misspelling Words 16 1.5 anaLyzIng your fIrst Program 11 Syntax 1.1: C++ program 12 Syntax 1.2: output statement 13 1 cfe2_ch01_p1_28.indd 1 10/25/10 1:03 PM Just as you gather tools, study a project, and make a plan for tackling it, in this chapter you will gather up the basics you need to start learning to program. after a brief introduction to computer hardware, software, and programming in general, you will learn how to write and run your first C++ program. You will also learn how to diagnose and fix programming errors, and how to use pseudocode to describe an algorithm—a step-by-step description of how to solve a problem—as you plan your programs. 1.1 What Is programming? Computers execute very basic instructions in rapid succession. a computer program is a sequence of instructions and decisions. You have probably used a computer for work or fun. Many people use computers for everyday tasks such as electronic banking or writing a term paper. Computers are good for such tasks. They can handle repetitive chores, such as totaling up numbers or placing words on a page, without getting bored or exhausted. The flexibility of a computer is quite an amazing phenomenon. The same machine can balance your checkbook, print your term paper, and play a game. In contrast, other machines carry out a much narrower range of tasks; a car drives and a toaster toasts. Computers can carry out a wide range of tasks because they execute different programs, each of which directs the computer to work on a specific task. The computer itself is a machine that stores data (numbers, words, pictures), interacts with devices (the monitor, the sound system, the printer), and executes programs. A computer program tells a computer, in minute detail, the sequence of steps that are needed to fulfill a task. The physical computer and peripheral devices are collectively called the hardware. The programs the computer executes are called the software. Today’s computer programs are so sophisticated that it is hard to believe that they are composed of extremely primitive operations. A typical operation may be one of the following: • Put a red dot at this screen position. • Add up these two numbers. • If this value is negative, continue the program at a certain instruction. programming is the act of designing and implementing computer programs. The computer user has the illusion of smooth interaction because a program contains a huge number of such operations, and because the computer can execute them at great speed. The act of designing and implementing computer programs is called programming. In this book, you will learn how to program a computer—that is, how to direct the computer to execute tasks. To write a computer game with motion and sound effects or a word processor that supports fancy fonts and pictures is a complex task that requires a team of many highly skilled programmers. Your first programming efforts will be more mundane. The concepts and skills you learn in this book form an important foundation, and you should not be disappointed if your first programs do not rival the sophisticated software that is familiar to you. Actually, you will find that there is an immense thrill even in simple programming tasks. It is an amazing experience to see the computer precisely and quickly carry out a task that would take you hours of drudgery, to 2 cfe2_ch01_p1_28.indd 2 10/25/10 1:03 PM 1.2 the anatomy of a Computer 3 make small changes in a program that lead to immediate improvements, and to see the computer become an extension of your mental powers. seLf CheCk 1. 2. 3. Practice It What is required to play music on a computer? Why is a CD player less flexible than a computer? What does a computer user need to know about programming in order to play a video game? Now you can try these exercises at the end of the chapter: R1.1, R1.4. 1.2 the anatomy of a Computer the central processing unit (Cpu) performs program control and data processing. storage devices include memory and secondary storage. To understand the programming process, you need to have a rudimentary understanding of the building blocks that make up a computer. We will look at a personal computer. Larger computers have faster, larger, or more powerful components, but they have fundamentally the same design. At the heart of the computer lies the central processing unit (CPU) (see Figure 1). It consists of a single chip, or a small number of chips. A computer chip (integrated circuit) is a component with a plastic or metal housing, metal connectors, and inside wiring made principally from silicon. For a CPU chip, the inside wiring is enormously complicated. For example, the Pentium chip (a popular CPU for personal computers at the time of this writing) is composed of several million structural elements, called transistors. The CPU performs program control and data processing. That is, the CPU locates and executes the program instructions; it carries out arithmetic operations such as addition, subtraction, multiplication, and division; it fetches data from external memory or devices and stores data back. The computer stores data and programs. There are two kinds of storage. Primary storage is made from memory chips: electronic circuits that can store data, provided they are supplied with electric power. Secondary storage, usually a hard disk, provides less expensive storage that persists without electricity. A hard disk consists of rotating platters, which are coated with a magnetic material, and read/write heads, which can detect and change the magnetic flux on the platters (see Figure 2). figure 1 Central processing unit cfe2_ch01_p1_28.indd 3 10/25/10 1:03 PM 4 Chapter 1 Introduction figure 2 a hard disk Programs and data are typically stored on the hard disk and loaded into memory when the program starts. The program then updates the data in memory and writes the modified data back to the hard disk. To interact with a human user, a computer requires peripheral devices. The computer transmits information (called output) to the user through a display screen, speakers, and printers. The user can enter information (called input) by using a keyboard or a pointing device such as a mouse. Some computers are self-contained units, whereas others are interconnected through networks. Through the network cabling, the computer can read data and programs from central storage locations or send data to other computers. For the user of a networked computer it may not even be obvious which data reside on the computer itself and which are transmitted through the network. Printer Mouse Ports Disk Controller CPU Graphics card Keyboard Hard disk CD/DVD drive Memory Monitor Sound card Speakers Network card figure 3 cfe2_ch01_p1_28.indd 4 Internet schematic design of a personal Computer 10/25/10 1:03 PM 1.2 the anatomy of a Computer 5 Figure 3 gives a schematic overview of the architecture of a personal computer. Program instructions and data (such as text, numbers, audio, or video) are stored on the hard disk, on an optical disk such as a DVD, or elsewhere on the network. When a program is started, it is brought into memory, where the CPU can read it. The CPU reads the program one instruction at a time. As directed by these instructions, the CPU reads data, modifies it, and stores it. Some program instructions will cause the CPU to place dots on the display screen or printer or to vibrate the speaker. As these actions happen many times over and at great speed, the human user perceives images and sound. Some program instructions read user input from the keyboard or mouse. The program analyzes the nature of these inputs and then executes the next appropriate instruction. seLf CheCk 4. 5. Practice It Where is a program stored when it is not currently running? Which part of the computer carries out arithmetic operations, such as addition and multiplication? Now you can try these exercises at the end of the chapter: R1.2, R1.3. Random Fact 1.1 the enIaC (electronic numerical integrator and computer) was the first usable electronic computer. It was designed by J. presper eckert and John Mauchly at the university of pennsylvania and was completed in 1946—two years before transistors were invented. the computer was housed in a large room and consisted of many cabinets containing about 18,000 vacuum tubes (see Figure 2). Vacuum tubes burned out at the rate of several tubes per day. an attendant with a shopping cart full of tubes constantly made the rounds and replaced defective ones. the computer was programmed by connecting wires on panels. each wiring configuration would set up the computer for a particular problem. to have the computer work on a different problem, the wires had to be replugged. Work on the enIaC was supported by the u.s. army, which was interested in computations of ballistic tables that would give the trajectory of a projectile, depending on the wind resistance, initial velocity, and atmospheric conditions. to compute the trajectories, cfe2_ch01_p1_28.indd 5 the enIaC and the dawn of Computing one must find the numerical solutions of certain differential equations; hence the name “numerical integrator”. Before machines like the enIaC were developed, humans did this kind of work, and until the 1950s the word “computer” referred to these people. the enIaC was later used for peaceful purposes, such as the tabulation of u.s. Census data. figure 4 the enIaC 10/25/10 1:03 PM 6 Chapter 1 Introduction 1.3 Machine Code and programming languages On the most basic level, computer instructions are extremely primitive. The processor executes machine instructions. A typical sequence of machine instructions is 1. Move the contents of memory location 40000 into the CPU. 2. If that value is > 100, continue with the instruction that is stored in memory location 11280. Computer programs are stored as machine instructions in a code that depends on the processor type. C++ is a generalpurpose language that is in widespread use for systems and embedded programming. Actually, machine instructions are encoded as numbers so that they can be stored in memory. On a Pentium processor, this sequence of instruction is encoded as the sequence of numbers 161 40000 45 100 127 11280 On a processor from a different manufacturer, the encoding would be different. When this kind of processor fetches this sequence of numbers, it decodes them and executes the associated sequence of commands. How can we communicate the command sequence to the computer? The simplest method is to place the actual numbers into the computer memory. This is, in fact, how the very earliest computers worked. However, a long program is composed of thousands of individual commands, and it is a tedious and error-prone affair to look up the numeric codes for all commands and place the codes manually into memory. As already mentioned, computers are really good at automating tedious and errorprone activities. It did not take long for computer scientists to realize that the computers themselves could be harnessed to help in the programming process. Computer scientists devised high level programming languages that allow programmers to describe tasks, using a syntax that is more closely related to the problems to be solved. In this book, we will use the C++ programming language, which was developed by Bjarne Stroustrup in the 1980s. Over the years, C++ has grown by the addition of many features. A standardization process culminated in the publication of the international C++ standard in 1998. A minor update to the standard was issued in 2003, and a major revision is expected to come to fruition around 2011. At this time, C++ is the most commonly Bjarne Stroustrup used language for developing system software such as databases and operating systems. Just as importantly, C++ is increasingly used for programming “embedded systems”, small computers that control devices such as automobile engines or cellular telephones. Here is a typical statement in C++: if (int_rate > 100) { cout << "Interest rate error"; } high-level programming languages are independent of the processor. cfe2_ch01_p1_28.indd 6 This means, “If the interest rate is over 100, display an error message”. A special computer program, a compiler, translates this high-level description into machine instructions for a particular processor. High-level languages are independent of the underlying hardware. C++ instructions work equally well on an Intel Pentium and a processor in a cell phone. Of course, the compiler-generated machine instructions are different, but the programmer who uses the compiler need not worry about these differences. 10/25/10 1:03 PM 1.4 6. seLf CheCk 7. 8. Practice It Becoming Familiar with Your programming environment 7 Is the compiler a part of the computer hardware or software? Does a person who uses a computer for office work ever run a compiler? What are the most important uses for C++? Now you can try these exercises at the end of the chapter: R1.5. Random Fact 1.2 two standards organizations, the american national standards Institute (ansI) and the International organization for standardization (Iso), have jointly developed the definitive standard for the C++ language. Why have standards? You encounter the benefits of standardization every day. When you buy a light bulb, you can be assured that it fits in the socket without having to measure the socket at home and the bulb in the store. In standards organizations fact, you may have experienced how painful the lack of standards can be if you have ever purchased a flashlight with nonstandard bulbs. replacement bulbs for such a flashlight can be difficult and expensive to obtain. the ansI and Iso standards organizations are associations of industry professionals who develop standards for everything from car tires and credit card shapes to programming languages. having a standard for a programming language such as C++ means that you can take a program that you developed on one system with one manufacturer’s compiler to a different system and be assured that it will continue to work. 1.4 Becoming Familiar with Your programming environment set aside some time to become familiar with the programming environment that you will use for your class work. Many students find that the tools they need as programmers are very different from the software with which they are familiar. You should spend some time making yourself familiar with your programming environment. Because computer systems vary widely, this book can only give an outline of the steps you need to follow. It is a good idea to participate in a hands-on lab, or to ask a knowledgeable friend to give you a tour. step 1 Start the C++ development environment. Computer systems differ greatly in this regard. On many computers there is an integrated development environment in which you can write and test your programs. On other computers you first launch an editor, a program that functions like a word processor, in which you can enter your C++ instructions; then open a console window and type commands to execute your program. You need to find out how to get started with your environment. step 2 Write a simple program. The traditional choice for the very first program in a new programming language is a program that displays a simple greeting: “Hello, World!”. Let us follow that tradition. Here is the “Hello, World!” program in C++: cfe2_ch01_p1_28.indd 7 10/25/10 1:03 PM 8 Chapter 1 Introduction #include <iostream> using namespace std; int main() { cout << "Hello, World!" << endl; return 0; } an editor is a program for entering and modifying text, such as a C++ program. C++ is case sensitive. You must be careful about distinguishing between upper- and lowercase letters. We will examine this program in the next section. No matter which programming environment you use, you begin your activity by typing the program statements into an editor window. Create a new file and call it hello.cpp, using the steps that are appropriate for your environment. (If your environment requires that you supply a project name in addition to the file name, use the name hello for the project.) Enter the program instructions exactly as they are given above. Alternatively, locate an electronic copy in the source files for the programs in this book and paste it into your editor. As you write this program, pay careful attention to the various symbols, and keep in mind that C++ is case sensitive. You must enter upper- and lowercase letters exactly as they appear in the program listing. You cannot type MAIN or Endl. If you are not careful, you will run into problems—see Common Error 1.2 on page 16. step 3 the compiler translates C++ programs into machine code. Compile and run the program. The process for building and running a C++ program depends greatly on your programming environment. In some integrated development environments, you simply push a button. In other environments, you may have to type commands. When you run the test program, the message Hello, World! will appear somewhere on the screen (see Figures 5 and 6). figure 5 running the hello program in an Integrated development environment cfe2_ch01_p1_28.indd 8 10/25/10 1:03 PM 1.4 Becoming Familiar with Your programming environment 9 figure 6 Compiling and running the hello program in a Console Window the linker combines machine code with library code into an executable program. It is useful to know what goes on behind the scenes when your program gets built. First, the compiler translates the C++ source code (that is, the statements that you wrote) into machine instructions. The machine code contains only the translation of the code that you wrote. That is not enough to actually run the program. To display a string on a window, quite a bit of low-level activity is necessary. The implementors of your C++ development environment provided a library that includes the definition of cout and its functionality. A library is a collection of code that has been programmed and translated by someone else, ready for you to use in your program. (More complicated programs are built from more than one machine code file and more than one library.) A program called the linker takes your machine code and the necessary parts from the C++ library and builds an executable file. (Figure 7 gives an overview of these steps.) The executable file is usually called hello.exe or hello, depending on your computer system. You can run the executable program even after you exit the C++ development environment. step 4 Organize your work. As a programmer, you write programs, try them out, and improve them. You store your programs in files. Files have names, and the rules for legal names differ from one system to another. Some systems allow spaces in file names; others don’t. Some distinguish between upper- and lowercase letters; others don’t. Most C++ compilers require that C++ files end in an extension .cpp, .cxx, .cc, or .C; for example, test.cpp. Files are stored in folders or directories. A folder can contain files as well as other folders, which themselves can contain more files and folders (see Figure 8). This hierarchy can be quite large, and you need not be concerned with all of its branches. Editor Compiler Linker Machine code Executable Program Source File Library files figure 7 cfe2_ch01_p1_28.indd 9 From source Code to executable program 10/25/10 1:03 PM 10 Chapter 1 Introduction p figure 8 develop a strategy for keeping backup copies of your work before disaster strikes. seLf CheCk However, you should create folders for organizing your work. It is a good idea to make a separate folder for your programming class. Inside that folder, make a separate folder for each assignment. Some programming environments place your programs into a default location if you don’t specify a folder yourself. In that case, you need to find out where those files are located. Be sure that you understand where your files are located in the folder hierarchy. This information is essential when you submit files for grading, and for making backup copies. You will spend many hours creating and improving C++ programs. It is easy to delete a file by accident, and occasionally files are lost because of a computer malfunction. To avoid the frustration of recreating lost files, get in the habit of making backup copies of your work on a memory stick or on another computer. 9. 10. 11. Practice It cfe2_ch01_p1_28.indd 10 a Folder hierarchy How are programming projects stored on a computer? What do you expect to see when you load an executable file into your text editor? What do you do to protect yourself from data loss when you work on programming projects? Now you can try these exercises at the end of the chapter: R1.6. 10/25/10 1:03 PM 1.5 programming tip 1.1 analyzing Your First program 11 Backup Copies Backing up files on a memory stick is an easy and convenient storage method for many people. Another increasingly popular form of backup is Internet file storage. Here are a few pointers to keep in mind. • Back up often. Backing up a file takes only a few seconds, and you will hate yourself if you have to spend many hours recreating work that you could have saved easily. I recommend that you back up your work once every thirty minutes. • Rotate backups. Use more than one directory for backups, and rotate them. That is, first back up onto the first directory. Then back up onto the second directory. Then use the third, and then go back to the first. That way you always have three recent backups. If your recent changes made matters worse, you can then go back to the older version. • Pay attention to the backup direction. Backing up involves copying files from one place to another. It is important that you do this right—that is, copy from your work location to the backup location. If you do it the wrong way, you will overwrite a newer file with an older version. • Check your backups once in a while. Double-check that your backups are where you think they are. There is nothing more frustrating than to find out that the backups are not there when you need them. • Relax, then restore. When you lose a file and need to restore it from backup, you are likely to be in an unhappy, nervous state. Take a deep breath and think through the recovery process before you start. It is not uncommon for an agitated computer user to wipe out the last backup when trying to restore a damaged file. 1.5 analyzing Your First program In this section, we will analyze the first C++ program in detail. Here again is the source code: ch01/hello.cpp 1 2 3 4 5 6 7 8 9 #include <iostream> using namespace std; int main() { cout << "Hello, World!" << endl; return 0; } The first line, #include <iostream> tells the compiler to include a service for “stream input/output”. You will learn in Chapter 8 what a stream is. For now, you should simply remember to add this line into all programs that perform input or output. cfe2_ch01_p1_28.indd 11 10/25/10 1:03 PM 12 Chapter 1 Introduction syntax 1.1 C++ program Every program includes one or more headers for required services such as input/output. #include <iostream> Every program has a main function. The statements of a function are enclosed in braces. Every program that uses standard services requires this directive. using namespace std; int main() { cout << "Hello, World!" << endl; return 0; } Replace this statement when you write your own programs. Each statement ends in a semicolon. See page 14. The next line, using namespace std; every C++ program contains a function called main. use cout and the << operator to display values on the screen. tells the compiler to use the “standard namespace”. Namespaces are a mechanism for avoiding naming conflicts in large programs. You need not be concerned about namespaces. For the programs that you will be writing in this book, you will always use the standard namespace. Simply add using namespace std; at the top of every program that you write, just below the #include directives. The construction int main() { ... return 0; } defines a function called main that “returns” an “integer” (that is, a whole number without a fractional part, called int in C++) with value 0. This value indicates that the program finished successfully. A function is a collection of programming instructions that carry out a particular task. Every C++ program must have a main function. Most C++ programs contain other functions besides main, but it will take us until Chapter 5 to discuss functions and return values. For now, it is a good idea to consider all these parts as the plumbing that is necessary to write a simple program. Simply place the code that you want to execute inside the braces of the main function. (The basic structure of a C++ program is shown in Syntax 1.1.) To display values on the screen, you use an entity called cout and the << operator (sometimes called the insertion operator). For example, the statement cout << 39 + 3; displays the number 42. cfe2_ch01_p1_28.indd 12 10/25/10 1:03 PM 1.5 syntax 1.2 analyzing Your First program 13 output statement Strings are enclosed in quotation marks. Data sent to cout is displayed in a console window. * denotes multiplication. cout << "The answer is" << 6 * 7 << endl; Add a << symbol before each item to be displayed. enclose text strings in quotation marks. use + to add two numbers and * to multiply two numbers. send endl to cout to end a line of displayed output. end each statement with a semicolon. seLf CheCk You can send strings and numbers to cout. Sending endl to cout starts a new line. The statement cout << "Hello"; displays the string Hello. A string is a sequence of characters. You must enclose the contents of a string inside quotation marks so that the compiler knows you literally mean the text "Hello" and not a function with the same name. You can send more than one item to cout. Use a << before each one of them. For example, cout << "The answer is " << 6 * 7; displays The answer is 42 (in C++, the * denotes multiplication). The endl symbol denotes an end of line marker. When this marker is sent to cout, the cursor is moved to the first column in the next screen row. If you don’t use an end of line marker, then the next displayed item will simply follow the current string on the same line. In this program we only printed one item, but in general we will want to print multiple items, and it is a good habit to end all lines of output with an end of line marker. Finally, note that each statement in C++ ends in a semicolon, just as every English sentence ends in a period. 12. 13. How do you modify the hello.cpp program to greet you instead? What is wrong with this program? #include <iostream> using namespace std; int main() { cout << Goodbye, World! << endl; return 0; } 14. What does the following sequence of statements print? cout << "Hello"; cout << "World"; cfe2_ch01_p1_28.indd 13 10/25/10 1:03 PM 14 Chapter 1 Introduction 15. What does the following statement print? cout << 2 * 2 << 2; 16. What does the following statement print? cout << "Hello" << endl << endl << "World"; Practice It Common error 1.1 Now you can try these exercises at the end of the chapter: R1.7, P1.1, P1.2. omitting semicolons In C++ every statement must end in a semicolon. Forgetting to type a semicolon is a common error. It confuses the compiler because the compiler uses the semicolon to find where one statement ends and the next one starts. The compiler does not use line ends or closing braces to recognize the ends of statements. For example, the compiler considers cout << "Hello, World!" << endl return 0; a single statement, as if you had written cout << "Hello, World!" << endl return 0; and then it doesn’t understand that statement, because it does not expect the word return in the middle of an output command. The remedy is simple. Just scan every statement for a terminating semicolon, just as you would check that every English sentence ends in a period. special topic 1.1 escape sequences How can you display a string containing quotation marks, such as Hello, "World" You can’t use cout << "Hello, "World""; As soon as the compiler reads "Hello, ", it thinks the string is finished, and then it gets all confused about World. Compilers have a one-track mind, and if a simple analysis of the input doesn’t make sense to them, they just refuse to go on, and they report an error. In contrast, a human would probably realize that the second and third quotation marks were supposed to be part of the string. Well, how do we then display quotation marks on the screen? The designers of C++ provided an escape hatch. Mark each quotation mark with a backslash character (\), like this: cout << "Hello, \"World\""; The sequence \" denotes a literal quote, not the end of a string. Such a sequence is called an escape sequence. There are a few other escape sequences. If you actually want to show a backslash on the display, you use the escape sequence \\. The statement cout << "Hello\\World"; prints Hello\World cfe2_ch01_p1_28.indd 14 10/25/10 1:03 PM 1.6 errors 15 Finally, the escape sequence \n denotes a newline character that starts a new line on the screen. The command cout << "Hello, World!\n"; has exactly the same effect as cout << "Hello, World!" << endl; 1.6 errors Programming languages follow very strict conventions. When you talk to another person, and you scramble or omit a word or two, your conversation partner will usually still understand what you have to say. But when you make an error in a C++ program, the compiler will not try to guess what you meant. (This is actually a good thing. If the compiler were to guess wrongly, the resulting program would do the wrong thing—quite possibly with disastrous effects.) In this section, you will learn how to cope with errors in your program. Experiment a little with the hello.cpp program. What happens if you make a typing error such as Programmers spend a fair amount of time fixing compiletime and run-time errors. cot << "Hello, World!" << endl; cout << "Hello, World! << endl; cout << "Hollo, World!" << endl; a compile-time error is a violation of the programming language rules that is detected by the compiler. a run-time error causes a program to take an action that the programmer did not intend. cfe2_ch01_p1_28.indd 15 In the first case, the compiler will complain that it has no clue what you mean by cot. The exact wording of the error message is dependent on the compiler, but it might be something like “Undefined symbol cot”. This is a compile-time error or syntax error. Something is wrong according to the language rules, and the compiler finds it. When the compiler finds one or more errors, it will not translate the program to machine code, and as a consequence there is no program to run. You must fix the error and compile again. It is common to go through several rounds of fixing compile-time errors before compilation succeeds for the first time. If the compiler finds an error, it will not simply stop and give up. It will try to report as many errors as it can find, so you can fix them all at once. Sometimes, however, one error throws it off track. This is likely to happen with the error in the second line. Since the programmer forgot the closing quote, the compiler will keep looking for the end of the string. In such cases, it is common for the compiler to emit bogus error reports for neighboring lines. You should fix only those error messages that make sense to you and then recompile. The error in the third line is of a different kind. The program will compile and run, but its output will be wrong. It will print Hollo, World! This is a run-time error. The program is syntactically correct and does something, but it doesn’t do what it is supposed to do. The compiler cannot find the error, and it must be flushed out when the program runs, by testing it and carefully looking at its 10/25/10 1:03 PM 16 Chapter 1 Introduction the programmer is responsible for inspecting and testing the program to guard against run-time errors. seLf CheCk output. Because run-time errors are caused by logical flaws in the program, they are often called logic errors. Some kinds of run-time errors are so severe that they generate an exception: a signal from the processor that aborts the program with an error message. For example, if your program includes the statement cout << 1 / 0; your program may terminate with a “divide by zero” exception. During program development, errors are unavoidable. Once a program is longer than a few lines, it requires superhuman concentration to enter it correctly without slipping up once. You will find yourself omitting semicolons or quotes more often than you would like, but the compiler will track down these problems for you. Run-time errors are more troublesome. The compiler will not find them—in fact, the compiler will cheerfully translate any program as long as its syntax is correct— but the resulting program will do something wrong. It is the responsibility of the program author to test the program and find any run-time errors. Program testing is an important topic that you will encounter many times in this book. 17. 18. 19. Practice It Common error 1.2 Suppose you omit the () characters after main from the hello.cpp program. Will you get a compile-time error or a run-time error? When you used your computer, you may have experienced a program that “crashed” (quit spontaneously) or “hung” (failed to respond to your input). Is that behavior a compile-time error or a run-time error? Why can’t you test a program for run-time errors when it has compiler errors? Now you can try these exercises at the end of the chapter: R1.10, R1.11. misspelling Words If you accidentally misspell a word, strange things may happen, and it may not always be completely obvious from the error messages what went wrong. Here is a good example of how simple spelling errors can cause trouble: #include <iostream> using namespace std; int Main() { cout << "Hello, World!" << endl; return 0; } This code defines a function called Main. The compiler will not consider this to be the same as the main function, because Main starts with an uppercase letter and the C++ language is casesensitive. Upper- and lowercase letters are considered to be completely different from each other, and to the compiler Main is no better match for main than rain. The compiler will compile your Main function, but when the linker is ready to build the executable file, it will complain about the missing main function and refuse to link the program. Of course, the message “missing main function” should give you a clue where to look for the error. If you get an error message that seems to indicate that the compiler is on the wrong track, it is a good idea to check for spelling and capitalization. In C++, most names use only lowercase letters. If you misspell the name of a symbol (for example out instead of cout), the compiler will complain about an “undefined symbol”. This error message is usually a good clue that you made a spelling error. cfe2_ch01_p1_28.indd 16 10/25/10 1:03 PM 1.7 problem solving: algorithm design 17 1.7 problem solving: algorithm design You will soon learn how to program calculations and decision making in C++. But before we look at the mechanics of implementing computations in the next chapter, let’s consider the planning process that precedes implementation. You may have run across advertisements that encourage you to pay for a computerized service that matches you up with a love partner. Think how this might work. You fill out a form and send it in. Others do the same. The data are processed by a computer program. Is it reasonable to assume that the computer can perform the task of finding the best match for you? Suppose your younger brother, not the computer, had all the forms on his desk. What instructions could you give him? You can’t say, Finding the perfect partner is not a “Find the best-looking person of the opposite problem that a computer can solve. sex who likes inline skating and browsing the Internet”. There is no objective standard for good looks, and your brother’s opinion (or that of a computer program analyzing the digitized photo) will likely be different from yours. If you can’t give written instructions for someone to solve the problem, there is no way the computer can magically solve the problem. The computer can only do what you tell it to do. It just does it faster, without getting bored or exhausted. Now consider the following investment problem: You put $10,000 into a bank account that earns 5 percent interest per year. How many years does it take for the account balance to be double the original? Could you solve this problem by hand? Sure. You figure out the balance as follows: year 0 1 2 3 4 interest 10000.00 x 0.05 = 500.00 10500.00 x 0.05 = 525.00 11025.00 x 0.05 = 551.25 11576.25 x 0.05 = 578.81 balance 10000 10000.00 + 500.00 = 10500.00 10500.00 + 525.00 = 11025.00 11025.00 + 551.25 = 11576.25 11576.25 + 578.81 = 12155.06 You keep going until the balance is at least $20,000. Then the last number in the year column is the answer. Of course, carrying out this computation is intensely boring to you or your younger brother. But computers are very good at carrying out repetitive calculations quickly and flawlessly. What is important to the computer is a description of the steps for finding the solution. Each step must be clear and unambiguous, requiring no guesswork. Here is such a description: Start with a year value of 0, a column for the interest, and a balance of $10,000. year 0 cfe2_ch01_p1_28.indd 17 interest balance 10000 10/25/10 1:03 PM 18 Chapter 1 Introduction Repeat the following steps while the balance is less than $20,000 Add 1 to the year value. Compute the interest as balance x 0.05 (i.e., 5 percent interest) Add the interest to the balance. year 0 1 interest 500.00 balance 10000 10500.00 14 15 942.82 989.96 19799.32 20789.28 Report the final year value as the answer. pseudocode is an informal description of a sequence of steps for solving a problem. Of course, these steps are not yet in a language that a computer can understand, but you will soon learn how to formulate them in C++. This informal description is called pseudocode. There are no strict requirements for pseudocode because it is read by human readers, not a computer program. Here are the kinds of pseudocode statements that we will use in this book: • Use statements such as the following to describe how a value is set or changed: total cost = purchase price + operating cost or Multiply the balance value by 1.05. or Remove the first and last character from the word. • You can describe decisions and repetitions as follows: If total cost 1 < total cost 2 While the balance is less than $20,000 For each picture in the sequence Use indentation to indicate which statements should be selected or repeated: For each car operating cost = 10 x annual fuel cost total cost = purchase price + operating cost Here, the indentation indicates that both statements should be executed for each car. • Indicate results with statements such as: Choose car1. Report the final year value as the answer. The exact wording is not important. What is important is that pseudocode describes a sequence of steps that is • Unambiguous • Executable • Terminating cfe2_ch01_p1_28.indd 18 10/25/10 1:03 PM 1.7 problem solving: algorithm design 19 figure 9 Understand the problem the software development process Develop and describe an algorithm Test the algorithm with simple inputs Translate the algorithm into C++ Compile and test your program an algorithm for solving a problem is a sequence of steps that is unambiguous, executable, and terminating. seLf CheCk A method is unambiguous when there are precise instructions for what to do at each step and where to go next. There is no room for guesswork or creativity. A method is executable when each step can be carried out in practice. Had we asked to use the actual interest rate that will be charged in years to come, and not a fixed rate of 5 percent per year, our method would not have been executable, because there is no way for anyone to know what that interest rate will be. A method is terminating if it will eventually come to an end. In our example, it requires a bit of thought to see that the method will not go on forever: With every step, the balance An algorithm is a recipe for goes up by at least $500, so eventually it must reach finding a solution. $20,000. A sequence of steps that is unambiguous, executable, and terminating is called an algorithm. We have found an algorithm to solve our investment problem, and thus we can find the solution by programming a computer. The existence of an algorithm is an essential prerequisite for programming a task. You need to first discover and describe an algorithm for the task that you want to solve before you start programming (see Figure 9). 20. 21. 22. Suppose the interest rate was 20 percent. How long would it take for the investment to double? Suppose your cell phone carrier charges you $29.95 for up to 300 minutes of calls, and $0.45 for each additional minute, plus 12.5 percent taxes and fees. Give an algorithm to compute the monthly charge from a given number of minutes. Consider the following pseudocode for finding the most attractive photo from a sequence of photos: Pick the first photo and call it "the best so far". For each photo in the sequence If it is more attractive than the "best so far" Discard "the best so far". Call this photo "the best so far". The photo called "the best so far" is the most attractive photo in the sequence. Is this an algorithm that will find the most attractive photo? cfe2_ch01_p1_28.indd 19 10/25/10 1:03 PM 20 Chapter 1 Introduction 23. 24. Suppose each photo in Self Check 22 had a price tag. Give an algorithm for finding the most expensive photo. Suppose you have a random sequence of black and white marbles and want to rearrange it so that the black and white marbles are grouped together. Consider this algorithm: Repeat until sorted Locate the first black marble that is preceded by a white marble, and switch them. 25. What does the algorithm do with the sequence mlmll? Spell out the steps until the algorithm stops. Suppose you have a random sequence of colored marbles. Consider this pseudocode: Repeat until sorted Locate the first marble that is preceded by a marble of a different color, and switch them. Why is this not an algorithm? Practice It hoW t o 1.1 Now you can try these exercises at the end of the chapter: R1.13, R1.14. describing an algorithm with Pseudocode Before you are ready to write a program in C++, you need to develop an algorithm—a method for arriving at a solution for a particular problem. Describe the algorithm in pseudocode: a sequence of precise steps formulated in English. For example, consider this problem: You have the choice of buying two cars. One is more fuel efficient than the other, but also more expensive. You know the price and fuel efficiency (in miles per gallon, mpg) of both cars. You plan to keep the car for ten years. Assume a price of $4 per gallon of gas and usage of 15,000 miles per year. You will pay cash for the car and not worry about financing costs. Which car is the better deal? step 1 Determine the inputs and outputs. In our sample problem, we have these inputs: • purchase price1 and fuel efficiency1, the price and fuel efficiency (in mpg) of the first car • purchase price2 and fuel efficiency2, the price and fuel efficiency of the second car We simply want to know which car is the better buy. That is the desired output. step 2 Break down the problem into smaller tasks. For each car, we need to know the total cost of driving it. Let’s do this computation separately for each car. Once we have the total cost for each car, we can decide which car is the better deal. The total cost for each car is purchase price + operating cost. We assume a constant usage and gas price for ten years, so the operating cost depends on the cost of driving the car for one year. The operating cost is 10 x annual fuel cost. The annual fuel cost is price per gallon x annual fuel consumed. The annual fuel consumed is annual miles driven / fuel efficiency. For example, if you drive the car for 15,000 miles and the fuel efficiency is 15 miles/gallon, the car consumes 1,000 gallons. cfe2_ch01_p1_28.indd 20 10/25/10 1:03 PM Chapter summary 21 step 3 Describe each subtask in pseudocode. In your description, arrange the steps so that any intermediate values are computed before they are needed in other computations. For example, list the step total cost = purchase price + operating cost after you have computed operating cost. Here is the algorithm for deciding which car to buy: For each car, compute the total cost as follows: annual fuel consumed = annual miles driven / fuel efficiency annual fuel cost = price per gallon x annual fuel consumed operating cost = 10 x annual fuel cost total cost = purchase price + operating cost If total cost1 < total cost2 Choose car1. Else Choose car2. step 4 Test your pseudocode by working a problem. We will use these sample values: Car 1: $25,000, 50 miles/gallon Car 2: $20,000, 30 miles/gallon Here is the calculation for the cost of the first car: annual fuel consumed = annual miles driven / fuel efficiency = 15000 / 50 = 300 annual fuel cost = price per gallon x annual fuel consumed = 4 x 300 = 1200 operating cost = 10 x annual fuel cost = 10 x 1200 = 12000 total cost = purchase price + operating cost = 25000 + 12000 = 37000 Similarly, the total cost for the second car is $40,000. Therefore, the output of the algorithm is to choose car 1. Worked exaMple 1.1 Writing an algorithm for tiling a floor This Worked Example shows how to develop an algorithm for laying tile in an alternating pattern of colors. Chapter suMMarY define “computer program” and programming. • Computers execute very basic instructions in rapid succession. • A computer program is a sequence of instructions and decisions. • Programming is the act of designing and implementing computer programs. describe the components of a computer. • The central processing unit (CPU) performs program control and data processing. • Storage devices include memory and secondary storage. Available online at www.wiley.com/college/horstmann. cfe2_ch01_p1_28.indd 21 10/25/10 1:03 PM 22 Chapter 1 Introduction describe the process of translating high-level languages to machine code. • Computer programs are stored as machine instructions in a code that depends on the processor type. • C++ is a general-purpose language that is in widespread use for systems and embedded programming. • High-level programming languages are independent of the processor. Become familiar with your C++ programming environment. • Set aside some time to become familiar with the programming environment that you will use for your class work. • An editor is a program for entering and modifying text, such as a C++ program. • C++ is case sensitive. You must be careful about distinguishing between upperand lowercase letters. • Develop a strategy for keeping backup copies of your work before disaster strikes. • The compiler translates C++ programs into machine code. • The linker combines machine code with library code into an executable program. describe the building blocks of a simple program. • • • • • • Every C++ program contains a function called main. Use cout and the << operator to display values on the screen. Enclose text strings in quotation marks. Use + to add two numbers and * to multiply two numbers. Send endl to cout to end a line of displayed output. End each statement with a semicolon. Classify program errors as compile-time and run-time errors. • A compile-time error is a violation of the programming language rules that is detected by the compiler. • A run-time error causes a program to take an action that the programmer did not intend. • The programmer is responsible for inspecting and testing the program to guard against run-time errors. Write pseudocode for simple algorithms. • Pseudocode is an informal description of a sequence of steps for solving a problem. • An algorithm for solving a problem is a sequence of steps that is unambiguous, executable, and terminating. cfe2_ch01_p1_28.indd 22 10/25/10 1:03 PM review exercises 23 reVIeW exerCIses r1.1 Explain the difference between using a computer program and programming a computer. r1.2 Which parts of a computer can store program code? Which can store user data? r1.3 Which parts of a computer serve to give information to the user? Which parts take user input? r1.4 A toaster is a single-function device, but a computer can be programmed to carry out different tasks. Is your cell phone a single-function device, or is it a programmable computer? (Your answer will depend on your cell phone model.) r1.5 Explain two benefits of using C++ over machine code. r1.6 On your own computer or on your lab computer, find the exact location (folder or directory name) of a. The sample file hello.cpp (after you saved it in your development environment). b. The standard header file <iostream>. r1.7 What does this program print? #include <iostream> using namespace std; int main() { cout << "6 * 7 = " << 6 * 7 << endl; return 0; } r1.8 What does this program print? #include <iostream> using namespace std; int main() { cout << "Hello" << "World" << endl; return 0; } Pay close attention to spaces. r1.9 What does this program print? #include <iostream> using namespace std; int main() { cout << "Hello" << endl << "World" << endl; return 0; } r1.10 Write three versions of the hello.cpp program that have different compile-time errors. Write a version that has a run-time error. r1.11 How do you discover compile-time errors? How do you discover run-time errors? cfe2_ch01_p1_28.indd 23 10/25/10 1:03 PM 24 Chapter 1 Introduction r1.12 Write an algorithm to settle the following question: A bank account starts out with $10,000. Interest is compounded monthly at 6 percent per year (0.5 percent per month). Every month, $500 is withdrawn to meet college expenses. After how many years is the account depleted? r1.13 Consider the question in Exercise R1.12. Suppose the numbers ($10,000, 6 percent, $500) were user selectable. Are there values for which the algorithm you developed would not terminate? If so, change the algorithm to make sure it always terminates. r1.14 In order to estimate the cost of painting a house, a painter needs to know the surface area of the exterior. Develop an algorithm for computing that value. Your inputs are the width, length, and height of the house, the number of windows and doors, and their dimensions. (Assume the windows and doors have a uniform size.) r1.15 You want to decide whether you should drive your car to work or take the train. You know the one-way distance from your home to your place of work, and the fuel efficiency of your car (in miles per gallon). You also know the one-way price of a train ticket. You assume the cost of gas at $4 per gallon, and car maintenance at 5 cents per mile. Write an algorithm to decide which commute is cheaper. r1.16 You want to find out which fraction of your car use is for commuting to work, and which is for personal use. You know the one-way distance from your home to your place of work. For a particular period, you recorded the beginning and ending mileage on the odometer and the number of work days. Write an algorithm to settle this question. r1.17 In the problem described in How To 1.1 on page 20, you made assumptions about the price of gas and the annual usage. Ideally, you would like to know which car is the better deal without making these assumptions. Why can’t a computer program solve that problem? r1.18 The value of p can be computed according to the following formula: 1 1 1 1 π = 1− + − + − 4 3 5 7 9 Write an algorithm to compute p. Because the formula is an infinite series and an algorithm must stop after a finite number of steps, you should stop when you have the result determined to six significant digits. r1.19 Suppose you put your younger brother in charge of backing up your work. Write a set of detailed instructions for carrying out his task. Explain how often he should do it, and what files he needs to copy from which folder to which location. Explain how he should verify that the backup was carried out correctly. engineering r1.20 The San Francisco taxi commission set the following rates for 2010: • First 1̸5th of a mile: $3.10 • Each additional 1̸5th of a mile or fraction thereof: $0.45 • Each minute of waiting or traffic delay: $0.45 The charge for “waiting or traffic delay” applies instead of the mileage charge for each minute in which the speed is slower than the break-even point. The break-even point is the speed at which 1̸5th of a mile is traversed in one minute. Develop an algorithm that yields the fare for traveling a given distance in a given amount of time, assuming that the taxi moves at a constant speed. cfe2_ch01_p1_28.indd 24 10/25/10 1:03 PM programming exercises 25 engineering r1.21 Suppose you know how long it takes a car to accelerate from 0 to 60 miles per hour. Develop an algorithm for computing the time required to travel a given distance (for example 5 miles), assuming that the car is initially at rest, accelerates to a given speed (for example 25 miles per hour), and drives at that speed until the distance is covered. Hint: An object that starts at rest and accelerates at a constant rate a for t seconds 1 travels a distance of s = at 2 . 2 proGraMMInG exerCIses P1.1 Write a program that prints a greeting of your choice, perhaps in another language. P1.2 Write a program that prints the message, “Hello, my name is Hal!” Then, on a new line, the program should print the message “What would you like me to do?” Then it’s the user’s turn to type in an input. You haven’t yet learned how to do it—just use the following lines of code: string user_input; getline(cin, user_input); Finally, the program should ignore the user input and print the message “I am sorry, I cannot do that.” This program uses the string data type. To access this feature, you must place the line #include <string> before the main function. Here is a typical program run. The user input is printed in color. Hello, my name is Hal! What would you like me to do? Clean up my room I am sorry, I cannot do that. When running the program, remember to press the Enter key after typing the last word of the input line. P1.3 Write a program that prints out a message “Hello, my name is Hal!” Then, on a new line, the program should print the message “What is your name?” As in Exercise P1.2, just use the following lines of code: string user_name; getline(cin, user_name); Finally, the program should print the message “Hello, user name. I am glad to meet you!” To print the user name, simply use cout << user_name; As in Exercise P1.2, you must place the line #include <string> before the main function. Here is a typical program run. The user input is printed in color. Hello, my name is Hal! What is your name? Dave Hello, Dave. I am glad to meet you! cfe2_ch01_p1_28.indd 25 10/25/10 1:03 PM 26 Chapter 1 Introduction P1.4 Write a program that prints the sum of the first ten positive integers, 1 + 2 + … + 10. P1.5 Write a program that prints the product of the first ten positive integers, 1 × 2 × … × 10. (Use * for multiplication in C++.) P1.6 Write a program that prints the balance of an account that earns 5 percent interest per year after the first, second, and third year. P1.7 Write a program that displays your name inside a box on the terminal screen, like this: Dave Do your best to approximate lines with characters such as | - +. P1.8 Write a program that prints your name in large letters, such as * * * * ***** * * * * ** * * * * ****** * * **** * * **** * * * * **** * * * * * * **** * * * * * * * * P1.9 Write a program that prints a face similar to (but different from) the following: ///// +-----+ (| o o |) | ^ | | '-' | +-----+ P1.10 Write a program that prints a house that looks exactly like the following: /\ / \ +----+ | .-.| | | || +-+-++ P1.11 Write a program that prints an animal speaking a greeting, similar to (but different from) the following: /\_/\ ----( ' ' ) / Hello \ ( - ) < Junior | | | | \ Coder!/ (__|__) ----- P1.12 Write a program that prints three items, such as the names of your three best friends or favorite movies, on three separate lines. P1.13 Write a program that prints a poem of your choice. If you don’t have a favorite poem, search the Internet for “Emily Dickinson” or “e e cummings”. P1.14 Write a program that prints an imitation of a Piet Mondrian painting. (Search the Internet if you are not familiar with his paintings.) Use character sequences such as @@@ or ::: to indicate different colors, and use - and | to form lines. P1.15 Write a program that prints the United States flag, using * and = characters. cfe2_ch01_p1_28.indd 26 10/25/10 1:03 PM answers to self-Check Questions 27 engineering P1.16 The atmospheres of the gas giant planets (Jupiter, Saturn, Uranus, and Neptune) are mostly comprised of hydrogen (H2) followed by helium (He). The atmospheres of the terrestrial planets are mostly comprised of carbon dioxide (CO2) followed by nitrogen (N2) for Venus and Mars, and for Earth, mostly Nitrogen (N2) followed by Oxygen (O2). Write a program that outputs this information in a chart with four columns for the type of planet, the name of the planet, its primary atmospheric gas, and secondary atmospheric gas. engineering P1.17 Write a program that displays the following image, using characters such as / \ - | + for the lines. Write Ω as “Ohm”. 12 V + – 5 kΩ 10 kΩ 6 kΩ 4 kΩ ansWers to selF-CheCk QuestIons 1. A program that reads the data on the CD and sends output to the speakers and the screen. 2. A CD player can do one thing—play music CDs. It cannot execute programs. 3. Nothing. 4. In secondary storage, typically a hard disk. 5. The central processing unit. 6. Software. 7. No—a compiler is intended for programmers, to translate high-level programming 8. 9. 10. 11. 12. instructions into machine code. System software and embedded systems Programs are stored in files, and files are stored in folders or directories. You will see a seemingly random sequence of characters and symbols. You back up your files and folders. Replace "World" with your name, for example: cout << "Hello, Harry!" << endl; 13. There are no quotes around Goodbye, World!. 14. It prints HelloWorld, without a space or comma. 15. 42, without a space. 16. Hello World with a blank line between the two words. 17. A compile-time error. 18. It is a run-time error. After all, the program had been compiled in order for you to run it. 19. When a program has compiler errors, no executable file is produced, and there is nothing to run. cfe2_ch01_p1_28.indd 27 10/25/10 1:03 PM 28 Chapter 1 Introduction 20. 4 years: 21. 22. 23. 24. 25. cfe2_ch01_p1_28.indd 28 0 10,000 1 12,000 2 14,400 3 17,280 4 20,736 Is the number of minutes at most 300? a. If so, the answer is $29.95 × 1.125 = $33.70. b. If not, 1. Compute the difference: (number of minutes) – 300. 2. Multiply that difference by 0.45. 3. Add $29.95. 4. Multiply the total by 1.125. That is the answer. No. The step If it is more attractive than the "best so far" is not executable because there is no objective way of deciding which of two photos is more attractive. Pick the first photo and call it "the most expensive so far". For each photo in the sequence If it is more expensive than "the most expensive so far" Discard "the most expensive so far". Call this photo "the most expensive so far". The photo called "the most expensive so far" is the most expensive photo in the sequence. The first black marble that is preceded by a white one is marked in blue: mlmll Switching the two yields lmmll The next black marble to be switched is lmmll yielding lmlml The next steps are llmml llmlm lllmm Now the sequence is sorted. The sequence doesn’t terminate. Consider the input mlmlm. The first two marbles keep getting switched. 10/25/10 1:03 PM Chapter 2 F u n d a m e n ta l d ata t y p e s Chapter Goals to be able to define and initialize variables and constants to understand the properties and limitations of integer and floating-point numbers to write arithmetic expressions and assignment statements in C++ to appreciate the importance of comments and good code layout to create programs that read and process input, and display the results to process strings, using the standard C++ string type Chapter Contents 2.1 Variables 30 Syntax 2.1: Variable definition 31 Syntax 2.2: assignment 34 Common Error 2.1: using undefined Variables 37 Common Error 2.2: using uninitialized Variables 37 Programming Tip 2.1: Choose descriptive Variable names 38 Special Topic 2.1: numeric types in C++ 38 Special Topic 2.2: numeric ranges and precisions 39 Programming Tip 2.2: do not use magic numbers 39 2.2 arithmetic 40 Common Error 2.3: unintended Integer division 43 Common Error 2.4: unbalanced parentheses 44 Common Error 2.5: Forgetting header Files 45 Common Error 2.6: roundoff errors 45 Programming Tip 2.3: spaces in expressions 46 Special Topic 2.3: Casts 46 Special Topic 2.4: Combining assignment and arithmetic 47 Random Fact 2.1: the pentium Floatingpoint Bug 47 2.3 input and Output 48 Syntax 2.3: Input statement 48 2.4 prOblem sOlVing: First dO it by hand 52 Worked Example 2.1: Computing travel time How To 2.1: Carrying out Computations 54 Worked Example 2.2: Computing the Cost of stamps 2.5 strings 56 Random Fact 2.2: International alphabets and unicode 61 29 cfe2_ch02_p29_74.indd 29 10/27/10 2:29 PM numbers and character strings (such as the ones on this display board) are important data types in any C++ program. In this chapter, you will learn how to work with numbers and text, and how to write simple programs that perform useful tasks with them. 2.1 Variables When your program carries out computations, you will want to store values so that you can use them later. In a C++ program, you use variables to store values. In this section, you will learn how to define and use variables. To illustrate the use of variables, we will develop a program that solves the following problem. Soft drinks are sold in cans and bottles. A store offers a sixpack of 12-ounce cans for the same price as a two-liter bottle. Which should you buy? (12 fluid ounces equal approximately 0.355 liters.) In our program, we will define variables for the number of cans per pack and for the volume of each can. Then we will compute the volume of a six-pack in What contains more soda? A six-pack of 12-ounce cans or a two-liter bottle? liters and print out the answer. 2.1.1 Variable definitions The following statement defines a variable named cans_per_pack: int cans_per_pack = 6; a variable is a storage location with a name. A variable is a storage location in a computer program. Each variable has a name and holds a value. A variable is similar to a parking space in a parking garage. The parking space has an identifier (such as “J 053”), and it can hold a vehicle. A variable has a name (such as cans_per_pack), and it can hold a value (such as 6). Like a variable in a computer program, a parking space has an identifier and a contents. 30 cfe2_ch02_p29_74.indd 30 10/27/10 2:29 PM 2.1 syntax 2.1 See page 33 for rules and examples of valid names. int cans_per_pack = 6; Use a descriptive variable name. See page 38. When defining a variable, you also specify the type of its values. 31 Variable definition Types introduced in this chapter are the number types int and double (page 32) and the string type (page 57). When defining a variable, you usually specify an initial value. Variables A variable definition ends with a semicolon. Supplying an initial value is optional, but it is usually a good idea. See page 37. When defining a variable, you usually want to initialize it. That is, you specify the value that should be stored in the variable. Consider again this variable definition: int cans_per_pack = 6; The variable cans_per_pack is initialized with the value 6. Like a parking space that is restricted to a certain type of vehicle (such as a compact car, motorcycle, or electric vehicle), a variable in C++ stores data of a specific type. C++ supports quite a few data types: numbers, text strings, files, dates, and many others. You must specify the type whenever you define a variable (see Syntax 2.1). The cans_per_pack variable is an integer, a whole number without a fractional part. In C++, this type is called int. (See the next section for more information about number types in C++.) Note that the type comes before the variable name: int cans_per_pack = 6; Table 1 shows variations of variable definitions. Each parking space is suitable for a particular type of vehicle, just as each variable holds a value of a particular type. cfe2_ch02_p29_74.indd 31 10/27/10 2:29 PM 32 Chapter 2 Fundamental data types table 1 Variable definitions in C++ Variable name ! Comment int cans = 6; Defines an integer variable and initializes it with 6. int total = cans + bottles; The initial value need not be a constant. (Of course, cans and bottles must have been previously defined.) int bottles = "10"; error: You cannot initialize a number with a string. int bottles; Defines an integer variable without initializing it. This can be a cause for errors—see Common Error 2.2 on page 37. int cans, bottles; Defines two integer variables in a single statement. In this book, we will define each variable in a separate statement. bottles = 1; caution: The type is missing. This statement is not a definition but an assignment of a new value to an existing variable—see Section 2.1.4 on page 34. 2.1.2 number types use the int type for numbers that cannot have a fractional part. In C++, there are several different types of numbers. You use the integer number type, called int in C++, to denote a whole number without a fractional part. For example, there must be an integer number of cans in any pack of cans—you cannot have a fraction of a can. table 2 number literals in C++ number cfe2_ch02_p29_74.indd 32 type Comment 6 int An integer has no fractional part. –6 int Integers can be negative. 0 int Zero is an integer. 0.5 double A number with a fractional part has type double. 1.0 double An integer with a fractional part .0 has type double. 1E6 double 2.96E-2 double A number in exponential notation: 1 × 106 or 1000000. Numbers in exponential notation always have type double. Negative exponent: 2.96 × 10–2 = 2.96 / 100 = 0.0296 100,000 error: Do not use a comma as a decimal separator. 3 1/2 error: Do not use fractions; use decimal notation: 3.5. 10/27/10 2:29 PM 2.1 use the double type for floating-point numbers. Variables 33 When a fractional part is required (such as in the number 0.355), we use floatingpoint numbers. The most commonly used type for floating-point numbers in C++ is called double. (If you want to know the reason, read Special Topic 2.1 on page 38.) Here is the definition of a floating-point variable: double can_volume = 0.355; When a value such as 6 or 0.355 occurs in a C++ program, it is called a number literal. Table 2 shows how to write integer and floating-point literals in C++. 2.1.3 Variable names When you define a variable, you should pick a name that explains its purpose. For example, it is better to use a descriptive name, such as can_volume, than a terse name, such as cv. In C++, there are a few simple rules for variable names: 1. Variable names must start with a letter or the underscore (_) character, and the remaining characters must be letters, numbers, or underscores. 2. You cannot use other symbols such as $ or %. Spaces are not permitted inside names either. You can use an underscore instead, as in can_volume. 3. Variable names are case-sensitive, that is, Can_volume and can_volume are different names. For that reason, it is a good idea to use only lowercase letters in variable names. 4. You cannot use reserved words such as double or return as names; these words are reserved exclusively for their special C++ meanings. (See Appendix B.) Table 3 shows examples of legal and illegal variable names in C++. table 3 Variable names in C++ Variable name ! cfe2_ch02_p29_74.indd 33 Comment can_volume1 Variable names consist of letters, numbers, and the underscore character. x In mathematics, you use short variable names such as x or y. This is legal in C++, but not very common, because it can make programs harder to understand (see Programming Tip 2.1 on page 38). Can_volume caution: Variable names are case-sensitive. This variable name is different from can_volume. 6pack error: Variable names cannot start with a number. can volume error: Variable names cannot contain spaces. double error: You cannot use a reserved word as a variable name. ltr/fl.oz error: You cannot use symbols such as / or. 10/27/10 2:29 PM 34 Chapter 2 Fundamental data types 2.1.4 the assignment statement an assignment statement stores a new value in a variable, replacing the previously stored value. You use the assignment statement to place a new value into a variable. Here is an example: cans_per_pack = 8; The left-hand side of an assignment statement consists of a variable. The right-hand side is an expression that has a value. That value is stored in the variable, overwriting its previous contents. There is an important difference between a variable definition and an assignment statement: int cans_per_pack = 6; // Variable definition ... cans_per_pack = 8; // Assignment statement the assignment operator = does not denote mathematical equality. The first statement is the definition of cans_per_pack. It is an instruction to create a new variable of type int, to give it the name cans_per_pack, and to initialize it with 6. The second statement is an assignment statement: an instruction to replace the contents of the existing variable cans_per_pack with another value. The = sign doesn’t mean that the left-hand side is equal to the right-hand side. The expression on the right is evaluated, and its value is placed into the variable on the left. Do not confuse this assignment operation with the = used in algebra to denote equality. The assignment operator is an instruction to do something, namely place a value into a variable. The mathematical equality states the fact that two values are equal. For example, in C++, it is perfectly legal to write total_volume = total_volume + 2; It means to look up the value stored in the variable total_volume, add 2 to it, and place the result back into total_volume. (See Figure 1.) The net effect of executing this statement is to increment total_volume by 2. For example, if total_volume was 2.13 before execution of the statement, it is set to 4.13 afterwards. Of course, in mathematics it would make no sense to write that x = x + 2. No value can equal itself plus 2. syntax 2.2 assignment This is an initialization of a new variable, NOT an assignment. The name of a previously defined variable double total = 0; This is an assignment. . . total = bottles * BOTTLE_VOLUME; . The expression that replaces . . total = total + cans * CAN_VOLUME; the previous value The same name can occur on both sides. See Figure 1. cfe2_ch02_p29_74.indd 34 10/27/10 2:29 PM 2.1 1 Variables 35 2 total_volume = 2.13 total_volume = 4.13 total_volume + 2 total_volume + 2 Figure 1 4.13 executing the assignment total_volume = total_volume + 2 2.1.5 Constants you cannot change the value of a variable that is defined as const. When a variable is defined with the reserved word const, its value can never change. Constants are commonly written using capital letters to distinguish them visually from regular variables: const double BOTTLE_VOLUME = 2; It is good programming style to use named constants in your program to explain the meanings of numeric values. For example, compare the statements double total_volume = bottles * 2; and double total_volume = bottles * BOTTLE_VOLUME; A programmer reading the first statement may not understand the significance of the number 2. The second statement, with a named constant, makes the computation much clearer. 2.1.6 Comments use comments to add explanations for humans who read your code. the compiler ignores comments. As your programs get more complex, you should add comments, explanations for human readers of your code. Here is an example: const double CAN_VOLUME = 0.355; // Liters in a 12-ounce can This comment explains the significance of the value 0.355 to a human reader. The compiler does not process comments at all. It ignores everything from a // delimiter to the end of the line. Just as a television commentator explains the news, you use comments in your program to explain its behavior. cfe2_ch02_p29_74.indd 35 10/27/10 2:29 PM 36 Chapter 2 Fundamental data types You use the // syntax for single-line comments. If you have a comment that spans multiple lines, enclose it between /* and */ delimiters. The compiler ignores these delimiters and everything in between. Here is a typical example, a long comment at the beginning of a program, to explain the program’s purpose: /* This program computes the volume (in liters) of a six-pack of soda cans and the total volume of a six-pack and a two-liter bottle. */ We are now ready to finish our program. The following program shows the use of variables, constants, and the assignment statement. The program displays the volume of a six-pack of cans and the total volume of the six-pack and a two-liter bottle. We use constants for the can and bottle volumes. The total_volume variable is initialized with the volume of the cans. Using an assignment statement, we add the bottle volume. ch02/volume1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <iostream> using namespace std; /* This program computes the volume (in liters) of a six-pack of soda cans and the total volume of a six-pack and a two-liter bottle. */ int main() { int cans_per_pack = 6; const double CAN_VOLUME = 0.355; // Liters in a 12-ounce can double total_volume = cans_per_pack * CAN_VOLUME; cout << "A six-pack of 12-ounce cans contains " << total_volume << " liters." << endl; const double BOTTLE_VOLUME = 2; // Two-liter bottle total_volume = total_volume + BOTTLE_VOLUME; cout << "A six-pack and a two-liter bottle contain " << total_volume << " liters." << endl; return 0; } program run A six-pack of 12-ounce cans contains 2.13 liters. A six-pack and a two-liter bottle contain 4.13 liters. selF check 1. 2. Define a variable suitable for holding the number of bottles in a case. What is wrong with the following variable definition? int ounces per liter = 28.35 cfe2_ch02_p29_74.indd 36 10/27/10 2:29 PM 2.1 3. 4. 5. 6. Variables 37 Define and initialize two variables, unit_price and quantity, to contain the unit price of a single bottle and the number of bottles purchased. Use reasonable initial values. Use the variables defined in Self Check 3 to display the total purchase price. Some drinks are sold in four-packs instead of six-packs. How would you change the volume1.cpp program to compute the total volume? What is wrong with this comment? double can_volume = 0.355; /* Liters in a 12-ounce can // 7. 8. 9. practice it Common error 2.1 Suppose the type of the cans_per_pack variable in the volume1.cpp program was changed from int to double. What would be the effect on the program? Why can’t the variable total_volume in the volume1.cpp program be declared as const? How would you explain assignment using the parking space analogy? Now you can try these exercises at the end of the chapter: R2.1, R2.2, P2.1. using undefined Variables You must define a variable before you use it for the first time. For example, the following sequence of statements would not be legal: double can_volume = 12 * liter_per_ounce; double liter_per_ounce = 0.0296; In your program, the statements are compiled in order. When the compiler reaches the first statement, it does not know that liter_per_ounce will be defined in the next line, and it reports an error. Common error 2.2 using uninitialized Variables If you define a variable but leave it uninitialized, then your program can act unpredictably. To understand why, consider what happens when you define a variable. Just enough space is set aside in memory to hold values of the type you specify. For example, with the definition int bottles; a block of memory big enough to hold integers is reserved. There is already some value in that memory. After all, you don’t get freshly minted transistors—just an area of memory that has previously been used, filled with flotsam left over from prior computations. (In this regard, a variable differs from a parking space. A parking space can be empty, containing no vehicle. But a variable always holds some value.) If you use the variable without initializing it, then that prior value will be used, yielding unpredictable results. For example, consider the program segment int bottles; // Forgot to initialize int bottle_volume = bottles * 2; // Result is unpredictable There is no way of knowing what value will be computed. If you are unlucky, a plausible value will happen to appear when you run the program at home, and an entirely different result will occur when the program is graded. cfe2_ch02_p29_74.indd 37 10/27/10 2:29 PM 38 Chapter 2 Fundamental data types programming tip 2.1 choose descriptive Variable names We could have saved ourselves a lot of typing by using shorter variable names, as in double cv = 0.355; Compare this definition with the one that we actually used, though. Which one is easier to read? There is no comparison. Just reading can_volume is a lot less trouble than reading cv and then figuring out it must mean “can volume”. In practical programming, this is particularly important when programs are written by more than one person. It may be obvious to you that cv stands for can volume and not current velocity, but will it be obvious to the person who needs to update your code years later? For that matter, will you remember yourself what cv means when you look at the code three months from now? special topic 2.1 numeric types in c++ In addition to the int and double types, C++ has several other numeric types. C++ has two floating-point types. The float type uses half the storage of the double type that we use in this book, but it can only store 6–7 digits. Many years ago, when computers had far less memory than they have today, float was the standard type for floating-point computations, and programmers would indulge in the luxury of “double precision” only when they needed the additional digits. Today, the float type is rarely used. By the way, these numbers are called “floating-point” because of their internal representation in the computer. Consider numbers 29600, 2.96, and 0.0296. They can be represented in a very similar way: namely, as a sequence of the significant digits—296—and an indication of the position of the decimal point. When the values are multiplied or divided by 10, only the position of the decimal point changes; it “floats”. Computers use base 2, not base 10, but the principle is the same. p table 4 number types cfe2_ch02_p29_74.indd 38 type typical range typical size int –2,147,483,648 … 2,147,483,647 (about 2 billion) 4 bytes unsigned 0 … 4,294,967,295 4 bytes short –32,768 … 32,767 2 bytes unsigned short 0 … 65,535 2 bytes long long –9,223,372,036,854,775,808 … 9,223,372,036,854,775,807 8 bytes double The double-precision floating-point type, with a range of about ±10308 and about 15 significant decimal digits 8 bytes float The single-precision floating-point type, with a range of about ±1038 and about 7 significant decimal digits 4 bytes 10/27/10 2:29 PM 2.1 Variables 39 In addition to the int type, C++ has integer types short, long, and long long. For each integer type, there is an unsigned equivalent. For example, the short type typically has a range from –32,768 to 32,767, whereas unsigned short has a range from 0 to 65,535. These strange-looking limits are the result of the use of binary numbers in computers. A short value uses 16 binary digits, which can encode 216 = 65,536 values. Keep in mind that the ranges for integer types are not standardized, and they differ among compilers. Table 4 contains typical values. special topic 2.2 numeric ranges and precisions Because numbers are represented in the computer with a limited number of digits, they cannot represent arbitrary integer or floating-point numbers. The int type has a limited range: On most platforms, it can represent numbers up to a little more than two billion. For many applications, this is not a problem, but you cannot use an int to represent the world population. If a computation yields a value that is outside the int range, the result overflows. No error is displayed. Instead, the result is truncated to fit into an int, yielding a useless value. For example, int one_billion = 1000000000; cout << 3 * one_billion << endl; displays –1294967296. In situations such as this, you can switch to double values. However, read Common Error 2.6 on page 45 for more information about a related issue: roundoff errors. programming tip 2.2 do not use magic numbers A magic number is a numeric constant that appears in your code without explanation. For example, total_volume = bottles * 2; Why 2? Are bottles twice as voluminous as cans? No, the reason is that every bottle contains 2 liters. Use a named constant to make the code self-documenting: const double BOTTLE_VOLUME = 2; total_volume = bottles * BOTTLE_VOLUME; There is another reason for using named constants. Suppose circumstances change, and the bottle volume is now 1.5 liters. If you used a named constant, you make a single change, and you are done. Otherwise, you have to look at every value of 2 in your program and ponder whether it means a bottle volume, or something else. In a program that is more than a few pages long, that is incredibly tedious and error-prone. Even the most reasonable cosmic constant is going to change one day. You think there are seven days per week? Your customers on Mars are going to be pretty unhappy about your silly prejudice. Make a constant const int DAYS_PER_WEEK = 7; cfe2_ch02_p29_74.indd 39 10/27/10 2:29 PM 40 Chapter 2 Fundamental data types 2.2 arithmetic In the following sections, you will learn how to carry out arithmetic and mathematical calculations in C++. 2.2.1 arithmetic operators use * for multiplication and / for division. C++ supports the same four basic arithmetic operations as a calculator—addition, subtraction, multiplication, and division—but it uses different symbols for multiplication and division. You must write a * b to denote multiplication. Unlike in mathematics, you can not write a b, a . b or a × b. Similarly, division is always indicated with a /, never a ÷ or a fraction bar. a+b becomes (a + b) / 2. For example, 2 Parentheses are used just as in algebra: to indicate in which order the subexpressions should be computed. For example, in the expression (a + b) / 2, the sum a + b is computed first, and then the sum is divided by 2. In contrast, in the expression a + b / 2 only b is divided by 2, and then the sum of a and b / 2 is formed. Just as in regular algebraic notation, multiplication and division have a higher precedence than addition and subtraction. For example, in the expression a + b / 2, the / is carried out first, even though the + operation occurs further to the left. If both arguments of an arithmetic operation are integers, the result is an integer. If one or both arguments are floatingpoint numbers, the result is a floating-point number. For example, 4 * 0.5 is 2.0. 2.2.2 Increment and decrement the ++ operator adds 1 to a variable; the -operator subtracts 1. Changing a variable by adding or subtracting 1 is so common that there is a special shorthand for it, namely counter++; counter--; The ++ increment operator gave the C++ programming language its name. C++ is the incremental improvement of the C language. 2.2.3 Integer division and remainder If both arguments of / are integers, the remainder is discarded. Division works as you would expect, as long as at least one of the numbers involved is a floating-point number. That is, 7.0 / 4.0, 7 / 4.0, and 7.0 / 4 all yield 1.75. However, if both numbers are integers, then the result of the division is always an integer, with the remainder discarded. That is, 7 / 4 evaluates to 1 because 7 divided by 4 is 1 with a remainder of 3 (which is discarded). This can be a source of subtle programming errors—see Common Error 2.3 on page 43. cfe2_ch02_p29_74.indd 40 10/27/10 2:29 PM 2.2 the % operator computes the remainder of an integer division. arithmetic 41 If you are interested in the remainder only, use the % operator: 7 % 4 is 3, the remainder of the integer division of 7 by 4. The % symbol has no analog in algebra. It was chosen because it looks similar to /, and the remainder operation is related to division. The operator is called modulus. (Some people call it modulo or mod.) It has no relationship with the percent operation that you find on some calculators. Here is a typical use for the integer / and % operations. Suppose you have an amount of pennies in a piggybank: int pennies = 1729; You want to determine the value in dollars and cents. You obtain the dollars through an integer division by 100. int dollars = pennies / 100; // Sets dollars to 17 The integer division discards the remainder. To obtain the remainder, use the % operator: int cents = pennies % 100; // Sets cents to 29 Another common use of the % operator is to check whether a number is even or odd. If a number n is even, then n % 2 is zero. Integer division and the % operator yield the dollar and cent values of a piggybank full of pennies. 2.2.4 Converting Floating-point numbers to Integers assigning a floatingpoint variable to an integer drops the fractional part. When a floating-point value is assigned to an integer variable, the fractional part is discarded: double price = 2.55; int dollars = price; // Sets dollars to 2 Discarding the fractional part is not always what you want. Often, you want to round to the nearest integer. To round a positive floating-point value to the nearest integer, add 0.5 and then convert to an integer: int dollars = price + 0.5; // Rounds to the nearest integer In our example, adding 0.5 turns all values above 2.5 into values above 3. In particular, 2.55 is turned into 3.05, which is then truncated to 3. (For a negative floating-point value, you subtract 0.5.) Because truncation is a potential cause for errors, your compiler may issue a warning that assigning a floating-point value to an integer variable is unsafe. See Special Topic 2.3 on page 46 on how to avoid this warning. 2.2.5 powers and roots In C++, there are no symbols for powers and roots. To compute them, you must call functions. To take the square root of a number, you use the sqrt function. For example, x is written as sqrt(x). To compute xn, you write pow(x, n). cfe2_ch02_p29_74.indd 41 10/27/10 2:29 PM 42 Chapter 2 Fundamental data types b * pow(1 + r / 100, n) r 100 1+ r 100  r   1 + 100  Figure 2 analyzing an expression the C++ library defines many mathematical functions such as sqrt (square root) and pow (raising to a power).  r  b × 1 +  100  n n To use the sqrt and pow functions, you must place the line #include <cmath> at the top of your program file. The header file <cmath> is a standard C++ header that is available with all C++ systems, as is <iostream>. As you can see, the effect of the /, sqrt, and pow operations is to flatten out mathematical terms. In algebra, you use fractions, exponents, and roots to arrange expressions in a compact two-dimensional form. In C++, you have to write all expressions in a linear arrangement. For example, the mathematical expression  r  b × 1 +  100  becomes n b * pow(1 + r / 100, n) Figure 2 shows how to analyze such an expression. table 5 arithmetic expressions mathematical expression C++ expression x+y 2 (x + y) / 2 xy 2 x * y / 2  r   1 + 100  cfe2_ch02_p29_74.indd 42 n Comments The parentheses are required; y x + y / 2 computes x + . 2 Parentheses are not required; operators with the same precedence are evaluated left to right. pow(1 + r / 100, n) Remember to add #include of your program. <cmath> to the top a 2 + b2 sqrt(a * a + b * b) a * a is simpler than pow(a, 2). i+ j+k 3 (i + j + k) / 3.0 If i, j, and k are integers, using a denominator of 3.0 forces floating-point division. 10/27/10 2:29 PM 2.2 arithmetic 43 table 6 other mathematical Functions Function description sin(x) sine of x (x in radians) cos(x) cosine of x tan(x) tangent of x log10(x) (decimal log ) abs(x) log10 ( x ) , x > 0 absolute value x Table 6 shows additional functions that are declared in the <cmath> header. Inputs and outputs are floating-point numbers. selF check 10. 11. 12. 13. 14. A bank account earns interest of p percent per year. In C++, how do you compute the interest earned in one year? Assume variables p and balance of type double have already been defined. In C++, how do you compute the side length of a square whose area is stored in the variable area? 4 The volume of a sphere is given by V = π r 3. 3 If the radius is given by a variable radius of type double, write a C++ expression for the volume. You may assume that p is defined by a constant PI. What is the value of 1729 / 10 and 1729 % 10? Suppose a punch recipe calls for a given amount of orange soda, measured in ounces. int amount = 32; We can compute the number of 12-ounce cans needed, assuming that the amount does not evenly divide into 12: int cans_needed = amount / 12 + 1; Use the % operator to determine how many ounces will be left over. For example, if 32 ounces are required, we need 3 cans and have 4 ounces left over. practice it Common error 2.3 Now you can try these exercises at the end of the chapter: R2.3, R2.5, P2.2. unintended integer division It is unfortunate that C++ uses the same symbol, namely /, for both integer and floating-point division. These are really quite different operations. It is a common error to use integer division by accident. Consider this segment that computes the average of three integers: cout << "Please enter your last three test scores: "; int s1; int s2; cfe2_ch02_p29_74.indd 43 10/27/10 2:29 PM 44 Chapter 2 Fundamental data types int s3; cin >> s1 >> s2 >> s3; double average = (s1 + s2 + s3) / 3; // Error cout << "Your average score is " << average << endl; What could be wrong with that? Of course, the average of s1, s2, and s3 is s1 + s2 + s3 3 Here, however, the / does not mean division in the mathematical sense. It denotes integer division because both s1 + s2 + s3 and 3 are integers. For example, if the scores add up to 14, the average is computed to be 4, the result of the integer division of 14 by 3. That integer 4 is then moved into the floating-point variable average. The remedy is to make the numerator or denominator into a floating-point number: double total = s1 + s2 + s3; double average = total / 3; or double average = (s1 + s2 + s3) / 3.0; Common error 2.4 unbalanced parentheses Consider the expression (-(b * b - 4 * a * c) / (2 * a) What is wrong with it? Count the parentheses. There are three ( and two ). The parentheses are unbalanced. This kind of typing error is very common with complicated expressions. Now consider this expression. -(b * b - (4 * a * c))) / (2 * a This expression has three ( and three ), but it still is not correct. In the middle of the expression, -(b * b - (4 * a * c))) / (2 * a ↑ there are only two ( but three ), which is an error. In the middle of an expression, the count of ( must be greater than or equal to the count of ), and at the end of the expression the two counts must be the same. Here is a simple trick to make the counting easier without using pencil and paper. It is difficult for the brain to keep two counts simultaneously. Keep only one count when scanning the expression. Start with 1 at the first opening parenthesis, add 1 whenever you see an opening parenthesis, and subtract one whenever you see a closing parenthesis. Say the numbers aloud as you scan the expression. If the count ever drops below zero, or is not zero at the end, the parentheses are unbalanced. For example, when scanning the previous expression, you would mutter -(b * b - (4 * a * c ) ) ) / (2 * a 1 2 1 0 –1 and you would find the error. cfe2_ch02_p29_74.indd 44 10/27/10 2:29 PM 2.2 Common error 2.5 arithmetic 45 Forgetting header Files Every program that carries out input or output needs the <iostream> header. If you use mathemat- ical functions such as sqrt, you need to include <cmath>. If you forget to include the appropriate header file, the compiler will not know symbols such as cout or sqrt. If the compiler complains about an undefined function or symbol, check your header files. Sometimes you may not know which header file to include. Suppose you want to compute the absolute value of an integer using the abs function. As it happens, this version of abs is not defined in the <cmath> header but in <cstdlib>. How can you find the correct header file? You need to locate the documentation of the abs function, preferably using the online help of your development environment or a reference site on the Internet such as http://www.cplusplus.com (see Figure 3). The documentation includes a short description of the function and the name of the header file that you must include. Figure 3 Common error 2.6 online documentation roundoff errors Roundoff errors are a fact of life when calculating with floating-point numbers. You probably have encountered that phenomenon yourself with manual calculations. If you calculate 1 3 to two decimal places, you get 0.33. Multiplying again by 3, you obtain 0.99, not 1.00. In the processor hardware, numbers are represented in the binary number system, not in decimal. You still get roundoff errors when binary digits are lost. They just may crop up at different places than you might expect. Here is an example. #include <iostream> using namespace std; cfe2_ch02_p29_74.indd 45 10/27/10 2:29 PM 46 Chapter 2 Fundamental data types int main() { double price = 4.35; int cents = 100 * price; // Should be 100 * 4.35 = 435 cout << cents << endl; // Prints 434! return 0; } Of course, one hundred times 4.35 is 435, but the program prints 434. Most computers represent numbers in the binary system. In the binary system, there is no exact representation for 4.35, just as there is no exact representation for 1/3 in the decimal system. The representation used by the computer is just a little less than 4.35, so 100 times that value is just a little less than 435. When a floating-point value is converted to an integer, the entire fractional part, which is almost 1, is thrown away, and the integer 434 is stored in cents. The remedy is to add 0.5 in order to round to the nearest integer: int cents = 100 * price + 0.5; programming tip 2.3 spaces in expressions It is easier to read x1 = (-b + sqrt(b * b - 4 * a * c)) / (2 * a); than x1=(-b+sqrt(b*b-4*a*c))/(2*a); Simply put spaces around all operators + - * / % =. However, don’t put a space after a unary minus: a – used to negate a single quantity, such as -b. That way, it can be easily distinguished from a binary minus, as in a - b. It is customary not to put a space after a function name. That is, write sqrt(x) and not sqrt (x). special topic 2.3 casts Occasionally, you need to store a value into a variable of a different type. Whenever there is the risk of information loss, the compiler issues a warning. For example, if you store a double value into an int variable, you can lose information in two ways: • The fractional part is lost. • The magnitude may be too large. For example, int n = 1.0E100; // NO is not likely to work, because 10100 is larger than the largest representable integer. Nevertheless, sometimes you do want to convert a floating-point value into an integer value. If you are prepared to lose the fractional part and you know that this particular floatingpoint number is not larger than the largest possible integer, then you can turn off the warning by using a cast. A cast is a conversion from one type (such as double) to another type (such as int) that is not safe in general, but that you know to be safe in a particular circumstance. You express a cast in C++ as follows: int cents = static_cast<int>(100 * price + 0.5); cfe2_ch02_p29_74.indd 46 10/27/10 2:29 PM 2.2 special topic 2.4 arithmetic 47 combining assignment and arithmetic In C++, you can combine arithmetic and assignment. For example, the instruction total += cans * CAN_VOLUME; is a shortcut for total = total + cans * CAN_VOLUME; Similarly, total *= 2; is another way of writing total = total * 2; Many programmers find this a convenient shortcut. If you like it, go ahead and use it in your own code. For simplicity, we won’t use it in this book, though. the pentium Floating-point Bug 1.333840000 1.333820000 ) 1.333800000 1.333780000 1.333760000 -1.30 -1.00 -0.70 -0.40 1.333740000 1.333720000 -0.10 1.333700000 0.20 0.50 1.333680000 -2.00 -1.60 3145727+ 1.40 -1.80 -1.00 -1.20 -1.40 -0.40 -0.60 4195835+ -0.80 0.20 0.00 1.10 -0.20 0.80 0.60 0.40 0.80 1.40 is mathematically equal to 0, and it did compute as 0 on a 486 processor. on his pentium processor the result was 256. as it turned out, Intel had independently discovered the bug in its testing and had started to produce chips that fixed it. the bug was caused by an error in a table that was used to speed up the processor’s floating-point multiplication algorithm. Intel determined that the problem was exceedingly rare. they claimed that under normal use, a typical consumer would only notice the problem once every 27,000 years. unfortunately for Intel, dr. nicely had not been a normal user. cfe2_ch02_p29_74.indd 47 Pentium FDIV error 1.20 ( 4,195,835 − ( 4,195,835 3,145,727) × 3,145,727 now Intel had a real problem on its hands. It figured that the cost of replacing all pentium processors that it had sold so far would cost a great deal of money. Intel already had more orders for the chip than it could produce, and it would be particularly galling to have to give out the scarce chips as free replacements instead of selling them. Intel’s management decided to punt and initially offered to replace the processors only for those customers who could prove that their work required absolute precision in mathematical calculations. naturally, that did not go over well with the hundreds of thousands of customers who had paid retail prices of $700 and more for a pentium chip and did not want to live with the nagging feeling that perhaps, one day, their income tax program would produce a faulty return. ultimately, Intel caved in to public demand and replaced all defective chips, at a cost of about 475 million dollars. 1.00 In 1994, Intel Corporation released what was then its most powerful processor, the pentium. unlike previous generations of its processors, it had a very fast floating-point unit. Intel’s goal was to compete aggressively with the makers of higher-end processors for engineering workstations. the pentium was a huge success immediately. In the summer of 1994, dr. thomas nicely of lynchburg College, Virginia, ran an extensive set of computations to analyze the sums of reciprocals of certain sequences of prime numbers. the results were not always what his theory predicted, even after he took into account the inevitable roundoff errors. then dr. nicely noted that the same program did produce the correct results when running on the slower 486 processor that preceded the pentium in Intel’s lineup. this should not have happened. the optimal roundoff behavior of floating-point calculations were standardized by the Institute for electrical and electronic engineers (Ieee) and Intel claimed to adhere to the Ieee standard in both the 486 and the pentium processors. upon further checking, dr. nicely discovered that there was a very small set of numbers for which the product of two numbers was computed differently on the two processors. For example, x/y Random Fact 2.1 This graph shows a set of numbers for which the original Pentium processor obtained the wrong quotient. 10/27/10 2:30 PM 48 Chapter 2 Fundamental data types 2.3 Input and output 2.3.1 Input In this section, you will see how to place user input into a variable. Consider for example the volume1.cpp program on page 36. Rather than assuming that the price for the two-liter bottle and the six-pack of cans are identical, we can ask the program user for the prices. When a program asks for user input, it should first print a message that tells the user which input is expected. Such a message is called a prompt. cout << "Please enter the number of bottles: "; // Display prompt use the >> operator to read a value and place it in a variable. Do not add an endl after the prompt. You want the input to appear after the colon, not on the following line. Next, the program issues a command to read the input. The cin object reads input from the console window. You use the >> operator (sometimes called the extraction operator) to place an input value into a variable, like this: int bottles; cin >> bottles; When the program executes the input statement, it waits for the user to provide input. The user also needs to hit the Enter key so that the program accepts the input. After the user supplies the input, the number is placed into the bottles variable, and the program continues. Note that in this code segment, there was no need to initialize the bottles variable because it is being filled by the very next statement. As a rule of thumb, you should initialize a variable when you declare it unless it is filled in an input statement that follows immediately. You can read more than one value in a single input statement: cout << "Please enter the number of bottles and cans: "; cin >> bottles >> cans; The user can supply both inputs on the same line: Please enter the number of bottles and cans: 2 6 Alternatively, the user can press the Enter key after each input: Please enter the number of bottles and cans: 2 6 syntax 2.3 Input statement Don't use endl here. Display a prompt in the console window. Define a variable to hold the input value. cout << "Enter the number of bottles: "; int bottles; cin >> bottles; The program waits for user input, then places the input into the variable. cfe2_ch02_p29_74.indd 48 10/27/10 2:30 PM 2.3 Input and output 49 2.3.2 Formatted output When you print the result of a computation, you often want some control over its appearance. For example, when you print an amount in dollars and cents, you usually want it to be rounded to two significant digits. That is, you want the output to look like Price per ounce: 0.04 instead of Price per ounce: 0.0409722 you use manipulators to specify how values should be formatted. The following command instructs cout to use two digits after the decimal point for all floating-point numbers: cout << fixed << setprecision(2); This command does not produce any output; it just manipulates cout so that it will change the output format. The values fixed and setprecision are called manipulators. We will discuss manipulators in detail in Chapter 8. For now, just remember to include the statement given above whenever you want currency values displayed neatly. To use manipulators, you must include the <iomanip> header in your program: #include <iomanip> You can combine the manipulators and the values to be displayed into a single statement. cout << fixed << setprecision(2) << "Price per ounce: " << price_per_ounce << endl; There is another manipulator that is sometimes handy. When you display several rows of data, you usually want the columns to line up. You use the setw manipulator to set the width of the next output field. The width is the total number of characters used for showing the value, including digits, the decimal point, and spaces. Controlling the width is important when you want columns of numbers to line up. For example, if you want a number to be printed in a column that is eight characters wide, you use cout << setw(8) << price_per_ounce; You use manipulators to line up your output in neat columns. cfe2_ch02_p29_74.indd 49 10/27/10 2:30 PM 50 Chapter 2 Fundamental data types This command prints the value price_per_ounce in a field of width 8, for example 0 . 0 4 (where each represents a space). There is a notable difference between the setprecision and setw manipulators. Once you set the precision, that value is used for all floating-point numbers. But the width affects only the next value. Subsequent values are formatted without added spaces. Our next example program will prompt for the price of a six-pack and the volume of each can, then print out the price per ounce. The program puts to work what you just learned about reading input and formatting output. ch02/volume2.cpp 1 2 3 4 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 30 31 32 33 #include <iostream> #include <iomanip> using namespace std; int main() { // Read price per pack cout << "Please enter the price for a six-pack: "; double pack_price; cin >> pack_price; // Read can volume cout << "Please enter the volume for each can (in ounces): "; double can_volume; cin >> can_volume; // Compute pack volume const double CANS_PER_PACK = 6; double pack_volume = can_volume * CANS_PER_PACK; // Compute and print price per ounce double price_per_ounce = pack_price / pack_volume; cout << fixed << setprecision(2); cout << "Price per ounce: " << price_per_ounce << endl; return 0; } program run Please enter the price for a six-pack: 2.95 Please enter the volume for each can (in ounces): 12 Price per ounce: 0.04 cfe2_ch02_p29_74.indd 50 10/27/10 2:30 PM 2.3 Input and output 51 table 7 Formatting output output statement output Comment cout << 12.345678; 12.3457 By default, a number is printed with 6 significant digits. cout << fixed << setprecision(2) << 12.3; 12.30 Use the fixed and setprecision manipulators to control the number of digits after the decimal point. cout << ":" << setw(6) << 12; : Four spaces are printed before the number, for a total width of 6 characters. cout << ":" << setw(2) << 123; :123 12 cout << setw(6) << ":" << 12; selF check 15. If the width not sufficient, it is ignored. :12.3 The width only refers to the next item. Here, the : is preceded by five spaces. What is wrong with the following statement sequence? cout << "Please enter the unit price: "; double unit_price; cin >> unit_price; int quantity; cin >> quantity; 16. What is problematic about the following statement sequence? cout << "Please enter the unit price: "; int unit_price; cin >> unit_price; 17. What is the output of the following statement sequence? double bottles = 10; cout << "The total volume is" << 2 * bottles; 18. 19. How do you print the floating-point variable total_price in dollars and cents, like this: $1.22? Using the setw manipulator, improve the output statement cout << "Bottles: " << bottles << endl << "Cans: " << cans << endl; so that the output looks like this: Bottles: Cans: 8 24 The numbers to the right should line up. (You may assume that the numbers have at most 8 digits.) practice it cfe2_ch02_p29_74.indd 51 Now you can try these exercises at the end of the chapter: R2.7, R2.8, P2.6, P2.7. 10/27/10 2:30 PM 52 Chapter 2 Fundamental data types 2.4 problem solving: First do It By hand A very important step for developing an algorithm is to first carry out the computations by hand. If you can’t compute a solution yourself, it’s unlikely that you’ll be able to write a program that automates the computation. To illustrate the use of hand calculations, consider the following problem. A row of black and white tiles needs to be placed along a wall. For aesthetic reasons, the architect has specified that the first and last tile shall be black. Your task is to compute the number of tiles needed and the gap at each end, given the space available and the width of each tile. Total width Gap pick concrete values for a typical situation to use in a hand calculation. To make the problem more concrete, let’s assume the following dimensions: • Total width: 100 inches • Tile width: 5 inches The obvious solution would be to fill the space with 20 tiles, but that would not work—the last tile would be white. Instead, look at the problem this way: The first tile must always be black, and then we add some number of white/black pairs: The first tile takes up 5 inches, leaving 95 inches to be covered by pairs. Each pair is 10 inches wide. Therefore the number of pairs is 95 / 10 = 9.5. However, we need to discard the fractional part since we can’t have fractions of tile pairs. Therefore, we will use 9 tile pairs or 18 tiles, together with the initial black tile. Altogether, we require 19 tiles. The tiles span 19 × 5 = 95 inches, leaving a total gap of 100 – 19 × 5 = 5 inches. The gap should be evenly distributed at both ends. At each end, the gap is (100 – 19 × 5) / 2 = 2.5 inches. This computation gives us enough information to devise an algorithm with arbitrary values for the total width and tile width. number of pairs = integer part of (total width - tile width) / (2 x tile width) number of tiles = 1 + 2 x number of pairs gap at each end = (total width - number of tiles x tile width) / 2 As you can see, doing a hand calculation gives enough insight into the problem that it becomes easy to develop an algorithm. cfe2_ch02_p29_74.indd 52 10/27/10 2:30 PM 2.4 20. selF check 21. 22. problem solving: First do It By hand 53 Translate the pseudocode for computing the number of tiles and the gap width into C++. Suppose the architect specifies a pattern with black, gray, and white tiles, like this: Again, the first and last tile should be black. How do you need to modify the algorithm? A robot needs to tile a floor with alternating black and white tiles. Develop an algorithm that yields the color (0 for black, 1 for white), given the row and column number. Start with specific values for the row and column, and then generalize. 1 2 3 4 1 2 3 4 r2 h2 23. h3 24. h1 For a particular car, repair and maintenance costs in year 1 are estimated at $100; in year 10, at $1,500. Assuming that the repair cost increases by the same amount every year, develop pseudocode to compute the repair cost in year 3 and then generalize to year n. The shape of a bottle is approximated by two cylinders of radius r1 and r2 and heights h1 and h2, joined by a cone section of height h3. Using the formulas for the volume of a cylinder,V = π r 2h, and a cone section, (r V =π 2 1 r1 practice it ) + r1r2 + r22 h , 3 develop pseudocode to compute the volume of the bottle. Using an actual bottle with known volume as a sample, make a hand calculation of your pseudocode. Now you can try these exercises at the end of the chapter: R2.13, R2.15, R2.16. Worked example 2.1 computing travel time In this Worked Example, we develop a hand calculation to compute the time that a robot requires to retrieve an item from rocky terrain. Available online at www.wiley.com/college/horstmann. cfe2_ch02_p29_74.indd 53 10/27/10 2:30 PM 54 Chapter 2 Fundamental data types hoW t o 2.1 carrying out computations Many programming problems require that you carry out arithmetic computations. This How To shows you how to turn a problem statement into pseudocode and, ultimately, a C++ program. For example, suppose you are asked to write a program that simulates a vending machine. A customer selects an item for purchase and inserts a bill into the vending machine. The vending machine dispenses the purchased item and gives change. We will assume that all item prices are multiples of 25 cents, and the machine gives all change in dollar coins and quarters. Your task is to compute how many coins of each type to return. step 1 Understand the problem: What are the inputs? What are the desired outputs? In this problem, there are two inputs: • The denomination of the bill that the customer inserts • The price of the purchased item There are two desired outputs: • The number of dollar coins that the machine returns • The number of quarters that the machine returns step 2 Work out examples by hand. Let’s assume that a customer purchased an item that cost $2.25 and inserted a $5 bill. The customer is due $2.75, or two dollar coins and three quarters. That is easy for you to see, but how can a C++ program come to the same conclusion? The computation is simpler if you work in pennies, not dollars. The amount due the customer is 275 pennies. Dividing by 100 yields 2, the number of dollars. Dividing the remainder (75) by 25 yields 3, the number of quarters. step 3 Write pseudocode for computing the answers. In the previous step, you worked out a specific instance of the problem. You now need to come up with a method that works in general. Given an arbitrary item price and payment, how can you compute the coins due? First, compute the amount due in pennies: amount due = 100 x bill value - item price in pennies To get the dollars, divide by 100 and discard the remainder: dollar coins = amount due / 100 (without remainder) A vending machine takes bills and gives change in coins. cfe2_ch02_p29_74.indd 54 10/27/10 2:30 PM 2.4 problem solving: First do It By hand 55 The remaining amount due can be computed in two ways. If you are familiar with the modulus operator, you can simply compute amount due = amount due % 100 Alternatively, subtract the penny value of the dollar coins from the amount due: amount due = amount due - 100 x dollar coins To get the quarters due, divide by 25: quarters = amount due / 25 step 4 Define the variables and constants that you need, and specify their types. Here, we have five variables: • bill_value • item_price • amount_due • dollar_coins • quarters Should we introduce constants to explain 100 and 25 as PENNIES_PER_DOLLAR and PENNIES_PER_ QUARTER? Doing so will make it easier to convert the program to international markets, so we will take this step. It is very important that amount_due and PENNIES_PER_DOLLAR are of type int because the computation of dollar_coins uses integer division. Similarly, the other variables are integers. step 5 Turn the pseudocode into C++ statements. If you did a thorough job with the pseudocode, this step should be easy. Of course, you have to know how to express mathematical operations (such as powers or integer division) in C++. amount_due = PENNIES_PER_DOLLAR * bill_value - item_price; dollar_coins = amount_due / PENNIES_PER_DOLLAR; amount_due = amount_due % PENNIES_PER_DOLLAR; quarters = amount_due / PENNIES_PER_QUARTER; step 6 Provide input and output. Before starting the computation, we prompt the user for the bill value and item price: cout << "Enter bill value (1 = $1 bill, 5 = $5 bill, etc.): "; cin >> bill_value; cout << "Enter item price in pennies: "; cin >> item_price; When the computation is finished, we display the result. For extra credit, we use the manipulator to make sure that the output lines up neatly. setw cout << "Dollar coins: " << setw(6) << dollar_coins << endl << "Quarters: " << setw(6) << quarters << endl; step 7 Include the required headers and provide a main function. We need the <iostream> header for all input and output. Because we use the setw manipulator, we also require <iomanip>. This program does not use any special mathematical functions. Therefore, we do not include the <cmath> header. In the main function, you need to define constants and variables (Step 4), carry out computations (Step 5), and provide input and output (Step 6). Clearly, you will want to first get the input, then do the computations, and finally show the output. Define the constants at the beginning of the function, and define each variable just before it is needed. cfe2_ch02_p29_74.indd 55 10/27/10 2:30 PM 56 Chapter 2 Fundamental data types Here is the complete program, ch02/vending.cpp: #include <iostream> #include <iomanip> using namespace std; int main() { const int PENNIES_PER_DOLLAR = 100; const int PENNIES_PER_QUARTER = 25; cout << "Enter bill value (1 = $1 bill, 5 = $5 bill, etc.): "; int bill_value; cin >> bill_value; cout << "Enter item price in pennies: "; int item_price; cin >> item_price; int amount_due = PENNIES_PER_DOLLAR * bill_value - item_price; int dollar_coins = amount_due / PENNIES_PER_DOLLAR; amount_due = amount_due % PENNIES_PER_DOLLAR; int quarters = amount_due / PENNIES_PER_QUARTER; cout << "Dollar coins: " << setw(6) << dollar_coins << endl << "Quarters: " << setw(6) << quarters << endl; } program run Enter bill value (1 = $1 bill, 5 = $5 bill, etc.): 5 Enter item price in pennies: 225 Dollar coins: 2 Quarters: 3 Worked example 2.2 computing the cost of stamps This Worked Example uses arithmetic functions to simulate a stamp vending machine. 2.5 strings strings are sequences of characters. Many programs process text, not numbers. Text consists of characters: letters, numbers, punctuation, spaces, and so on. A string is a sequence of characters. For example, the string "Harry" is a sequence of five characters. Available online at www.wiley.com/college/horstmann. cfe2_ch02_p29_74.indd 56 10/27/10 2:30 PM 2.5 strings 57 2.5.1 the string type You can define variables that hold strings. string name = "Harry"; The string type is a part of the C++ standard. To use it, simply include the header file, <string>: #include <string> We distinguish between string variables (such as the variable name defined above) and string literals (character sequences enclosed in quotes, such as "Harry"). The string stored in a string variable can change. A string literal denotes a particular string, just as a number literal (such as 2) denotes a particular number. Unlike number variables, string variables are guaranteed to be initialized even if you do not supply an initial value. By default, a string variable is set to an empty string: a string containing no characters. An empty string literal is written as "". The definition string response; has the same effect as string response = ""; 2.5.2 Concatenation use the + operator to concatenate strings; that is, to put them together to yield a longer string. Given two strings, such as "Harry" and "Morgan", you can concatenate them to one long string. The result consists of all characters in the first string, followed by all characters in the second string. In C++, you use the + operator to concatenate two strings. For example, string fname = "Harry"; string lname = "Morgan"; string name = fname + lname; results in the string "HarryMorgan" What if you’d like the first and last name separated by a space? No problem: string name = fname + " " + lname; This statement concatenates three strings: fname, the string literal " ", and lname. The result is "Harry Morgan" 2.5.3 string Input You can read a string from the console: cout << "Please enter your name: "; string name; cin >> name; cfe2_ch02_p29_74.indd 57 10/27/10 2:30 PM 58 Chapter 2 Fundamental data types When a string is read with the >> operator, only one word is placed into the string variable. For example, suppose the user types Harry Morgan as the response to the prompt. This input consists of two words. After the call cin >> name, the string "Harry" is placed into the variable name. Use another input statement to read the second word. 2.5.4 string Functions the length member function yields the number of characters in a string. The number of characters in a string is called the length of the string. For example, the length of "Harry" is 5. You can compute the length of a string with the length function. Unlike the sqrt or pow function, the length function is invoked with the dot notation. That is, you write the string whose length you want, then a period, then the name of the function, followed by parentheses: int n = name.length(); a member function is invoked using the dot notation. Many C++ functions require you to use this dot notation, and you must memorize (or look up) which do and which don’t. These functions are called member functions. We say that the member function length is invoked on the variable name. Once you have a string, you can extract substrings by using the substr member function. The member function call s.substr(start, length) returns a string that is made from the characters in the string s, starting at character start, and containing length characters. Here is an example: string greeting = "Hello, World!"; string sub = greeting.substr(0, 5); // sub is "Hello" use the substr member function to extract a substring of a string. The substr operation makes a string that consists of five characters taken from the string greeting. Indeed, "Hello" is a string of length 5 that occurs inside greeting. A curious aspect of the substr operation is the starting position. Starting position 0 means “start at the beginning of the string”. The first position in a string is labeled 0, the second one 1, and so on. For example, here are the position numbers in the greeting string: W o r l d ! H e l l o , 0 1 2 3 4 5 6 7 8 9 10 11 12 The position number of the last character (12) is always one less than the length of the string. Let’s figure out how to extract the substring "World". Count characters starting at 0, not 1. You find that W, the 8th character, has position number 7. The string you want is 5 characters long. Therefore, the appropriate substring command is string w = greeting.substr(7, 5); 5 cfe2_ch02_p29_74.indd 58 H e l l o , 0 1 2 3 4 5 6 l d ! W o r 7 8 9 10 11 12 10/27/10 2:30 PM 2.5 strings 59 If you omit the length, you get all characters from the given position to the end of the string. For example, greeting.substr(7) is the string "World!" (including the exclamation mark). Here is a simple program that puts these concepts to work. The program asks for your name and that of your significant other. It then prints out your initials. The operation first.substr(0, 1) makes a string consisting of one character, taken from the start of first. The program does the same for the second. Then it concatenates the resulting one-character strings with the string literal "&" to get a string of length 3, the initials string. (See Figure 4.) first = R 0 o 1 d 2 o 3 l 4 second = S 0 a 1 l 2 l 3 y 4 initials = R 0 & 1 S 2 Figure 4 f 5 o 6 Building the initials string Initials are formed from the first letter of each name. ch02/initials.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> #include <string> using namespace std; int main() { cout << "Enter your first name: "; string first; cin >> first; cout << "Enter your significant other's first name: "; string second; cin >> second; string initials = first.substr(0, 1) + "&" + second.substr(0, 1); cout << initials << endl; return 0; } program run Enter your first name: Rodolfo Enter your significant other's first name: Sally R&S cfe2_ch02_p29_74.indd 59 10/27/10 2:30 PM 60 Chapter 2 Fundamental data types table 8 string operations statement result Comment string str = "C"; str = str + "++"; str is set to "C++" When applied to strings, + denotes concatenation. string str = "C" + "++"; error error: You cannot concatenate cout << "Enter name: "; cin >> name; name contains "Harry" The >> operator places the next word into the string variable. name contains "Harry", last_name contains "Morgan" Use multiple >> operators to read more than one word. string greeting = "H & S"; int n = greeting.length(); n is set to 5 Each space counts as one character. string str = "Sally"; string str2 = str.substr(1, 3); str2 is set to "all" Extracts the substring of length 3 starting at position 1. (The initial position is 0.) string str = "Sally"; string str2 = str.substr(1); str2 is set to "ally" If you omit the length, all characters from the position until the end are included. string a = str.substr(0, 1); a is set to the initial letter in str Extracts the substring of length 1 starting at position 0. string b = str.substr(str.length() - 1); b is set to the last letter in str The last letter has position str.length() - 1. We need not specify the length. two string literals. (User input: Harry Morgan) cout << "Enter name: "; cin >> name >> last_name; (User input: Harry Morgan) selF check 25. 26. What is the length of the string "C++ Consider this string variable. Program"? string str = "C++ Program"; 27. 28. Give a call to the substr member function that returns the substring "gram". Use string concatenation to turn the string variable str from Self Check 26 to "C++ Programming". What does the following statement sequence print? string str = "Harry"; cout << str.substr(0, 1) + str.substr(str.length() - 1); 29. practice it cfe2_ch02_p29_74.indd 60 Give an input statement to read a name of the form “John Q. Public”. Now you can try these exercises at the end of the chapter: R2.6, R2.9, P2.12, P2.19. 10/27/10 2:30 PM 2.5 Random Fact 2.2 strings 61 International alphabets and unicode the english alphabet is pretty simple: upper- and lowercase a to z. other european languages have accent marks and special characters. For example, German has three so-called umlaut characters, ä, ö, ü, and a double-s character ß. these are not optional frills; you couldn’t write a page of German text without using these characters a few times. German keyboards have keys for these characters. Hebrew, Arabic, and English The German Keyboard Layout this poses a problem for computer users and designers. the american standard character encoding (called asCII, for american standard Code for Information Interchange) specifies 128 codes: 52 upper- and lowercase characters, 10 digits, 32 typographical symbols, and 34 control characters (such as space, newline, and 32 others for controlling printers and other devices). the umlaut and double-s are not among them. some German data processing systems replace seldom-used asCII characters with German letters: [ \ ] { | } ~ are replaced with Ä Ö Ü ä ö ü ß. While most people can live without these characters, C++ programmers definitely cannot. other encoding schemes take advantage of the fact that one byte can encode 256 different characters, of which only 128 are standardized by asCII. unfortunately, there are multiple incompatible standards for such encodings, resulting in a certain amount of aggravation among european computer users. many countries don’t use the roman script at all. russian, Greek, hebrew, arabic, and thai letters, to name just a few, have completely different shapes. to complicate matters, hebrew and arabic are typed from right to left. each of these alphabets has between 30 and 100 letters, and the countries using them have established encoding standards for them. cfe2_ch02_p29_74.indd 61 the situation is much more dramatic in languages that use the Chinese script: the Chinese dialects, Japanese, and korean. the Chinese script is not alphabetic but ideographic. a character represents an idea or thing. most words are made up of one, two, or three of these ideographic characters. (over 50,000 ideographs are known, of which about 20,000 are in active use.) therefore, two bytes are needed to encode them. China, taiwan, Japan, and korea have incompatible encoding standards for them. (Japanese and korean writing uses a mixture of native syllabic and Chinese ideographic characters.) the inconsistencies among character encodings have been a major nuisance for international electronic communication and for software manufacturers vying for a global market. starting in 1988, a consortium of hardware and software manufacturers developed a uniform 21-bit encoding scheme called unicode that is capable of encoding text in essentially all written languages of the world. about 100,000 characters have been given codes, including more than 70,000 Chinese, Japanese, and korean ideographs. there are even plans to add codes for extinct languages, such as egyptian hieroglyphs. The Chinese Script 10/27/10 2:30 PM 62 Chapter 2 Fundamental data types Chapter summary Write variable definitions in c++. • A variable is a storage location with a name. • When defining a variable, you usually specify an initial value. • When defining a variable, you also specify the type of its values. • Use the int type for numbers that cannot have a fractional part. • Use the double type for floating-point numbers. • An assignment statement stores a new value in a variable, replacing the previously stored value. • The assignment operator = does not denote mathematical equality. • You cannot change the value of a variable that is defined as const. • Use comments to add explanations for humans who read your code. The compiler ignores comments. use the arithmetic operations in c++. • • • • • Use * for multiplication and / for division. The ++ operator adds 1 to a variable; the -- operator subtracts 1. If both arguments of / are integers, the remainder is discarded. The % operator computes the remainder of an integer division. Assigning a floating-point variable to an integer drops the fractional part. • The C++ library defines many mathematical functions such as sqrt (square root) and pow (raising to a power). Write programs that read user input and write formatted output. • Use the >> operator to read a value and place it in a variable. • You use manipulators to specify how values should be formatted. carry out hand calculations when developing an algorithm. • Pick concrete values for a typical situation to use in a hand calculation. Write programs that process strings. • Strings are sequences of characters. • Use the + operator to concatenate strings; that is, put them together to yield a longer string. • The length member function yields the number of characters in a string. cfe2_ch02_p29_74.indd 62 10/27/10 2:30 PM review exercises 63 • A member function is invoked using the dot notation. • Use the substr member function to extract a substring of a string reVIeW exerCIses r2.1 What is the value of mystery after this sequence of statements? int mystery = 1; mystery = 1 - 2 * mystery; mystery = mystery + 1; r2.2 What is wrong with the following sequence of statements? int mystery = 1; mystery = mystery + 1; int mystery = 1 - 2 * mystery; r2.3 Write the following mathematical expressions in C++. 1 s = s0 + v0t + gt 2 2 G = 4π 2 a3 p 2 (m1 + m2 )  INT  FV = PV ⋅  1 +  1000  c = YRS a 2 + b2 − 2ab cos γ r2.4 Write the following C++ expressions in mathematical notation. a. dm = m * (sqrt(1 + v / c) / sqrt(1 - v / c) - 1); b. volume = PI * r * r * h; c. volume = 4 * PI * pow(r, 3) / 3; d. z = sqrt(x * x + y * y); r2.5 What are the values of the following expressions? In each line, assume that double x = 2.5; double y = -1.5; int m = 18; int n = 4; a. x + n * y - (x + n) * y b. m / n + m % n c. 5 * x - n / 5 d. 1 - (1 - (1 - (1 - (1 - n)))) e. sqrt(sqrt(n)) r2.6 What are the values of the following expressions? In each line, assume that string s = "Hello"; string t = "World"; cfe2_ch02_p29_74.indd 63 10/27/10 2:30 PM 64 Chapter 2 Fundamental data types a. s.length() + t.length() b. s.substr(1, 2) c. s.substr(s.length() / 2, 1) d. s + t e. t + s r2.7 Find at least five compile-time errors in the following program. #include iostream int main(); { cout << "Please enter two numbers:" cin << x, y; cout << "The sum of << x << "and" << y << "is: " x + y << endl; return; } r2.8 Find at least four run-time errors in the following program. #include <iostream> using namespace std; int main() { int total; int x1; cout << "Please enter a number:"; cin >> x1; total = total + x1; cout << "Please enter another number:"; int x2; cin >> x2; total = total + x1; double average = total / 2; cout << "The average of the two numbers is " << average << "endl"; return 0; } r2.9 Explain the differences between 2, 2.0, "2", and "2.0". r2.10 Explain what each of the following program segments computes. a. int x = 2; int y = x + x; b. string s = "2"; string t = s + s; r2.11 Write pseudocode for a program that reads a word and then prints the first character, the last character, and the characters in the middle. For example, if the input is Harry, the program prints H y arr. r2.12 Write pseudocode for a program that reads a name (such as Harold James Morgan) and then prints a monogram consisting of the initial letters of the first, middle, and last names (such as HJM). cfe2_ch02_p29_74.indd 64 10/27/10 2:30 PM review exercises 65 r2.13 Write pseudocode for a program that computes the first and last digit of a number. For example, if the input is 23456, the program should print out 2 and 6. Hint: %, log10. r2.14 Modify the pseudocode for the program in How To 2.1 so that the program gives change in quarters, dimes, and nickels. You can assume that the price is a multiple of 5 cents. To develop your pseudocode, first work with a couple of specific values. r2.15 A cocktail shaker is composed of three cone sections. Using realistic values for the radii and heights, compute the total volume, using the formula given in Self Check 24 for a cone section. Then develop an algorithm that works for arbitrary dimensions. r2.16 You are cutting off a piece of pie like this, where c is the length of the straight part (called the chord length) and h is the height of the piece. c h d h3 There is an approximate formula for the area: A ≈ 2 ch + 3 2c However, h is not so easy to measure, whereas the diameter d of a pie is usually well-known. Calculate the area where the diameter of the pie is 12 inches and the chord length of the segment is 10 inches. Generalize to an algorithm that yields the area for any diameter and chord length. r2.17 The following pseudocode describes how to obtain the name of a day, given the day number (0 = Sunday, 1 = Monday, and so on.) Define a string called names containing "SunMonTueWedThuFriSat". Compute the starting position as 3 x the day number. Extract the substring of names at the starting position with length 3. Check this pseudocode, using the day number 4. Draw a diagram of the string that is being computed, similar to Figure 4 on page 59. r2.18 The following pseudocode describes how to swap two letters in a word. We are given a string str and two positions i and j. (i comes before j) Set first to the substring from the start of the string to the last position before i. Set middle to the substring from positions i + 1 to j - 1. Set last to the substring from position j + 1 to the end of the string. Concatenate the following five strings: first, the string containing just the character at position j, middle, the string containing just the character at position i, and last. Check this pseudocode, using the string "Gateway" and positions 2 and 4. Draw a diagram of the string that is being computed, similar to Figure 4 on page 59. cfe2_ch02_p29_74.indd 65 10/27/10 2:30 PM 66 Chapter 2 Fundamental data types r2.19 Run the following program, and explain the output you get. #include <iostream> using namespace std; int main() { int total; cout << "Please enter a number: "; double x1; cin >> x1; total = total + x1; cout << "total: " << total << endl; cout << "Please enter a number: "; double x2; cin >> x2; total = total + x2; cout << "total: " << total << endl; total = total / 2; cout << "total: " << total << endl; cout << "The average is " << total << endl; return 0; } Note the trace messages (in blue) that are inserted to show the current contents of the total variable. How do you fix the program? (The program has two separate errors.) r2.20 Write a program that prints the values 3 * 1000 * 1000 * 1000 3.0 * 1000 * 1000 * 1000 Explain the results. r2.21 This chapter contains a number of recommendations regarding variables and con- stants that make programs easier to read and maintain. Briefly summarize these recommendations. proGrammInG exerCIses p2.1 Write a program that displays the dimensions of a letter-size (8.5 × 11 inches) sheet of paper in millimeters. There are 25.4 millimeters per inch. Use constants and comments in your program. p2.2 Write a program that computes and displays the circumference of a letter-size (8.5 × 11 inches) sheet of paper and the length of its diagonal. p2.3 Write a program that reads a number and displays the square, cube, and fourth power. Use the pow function only for the fourth power. p2.4 Write a program that prompts the user for two integers and then prints • The sum • The difference cfe2_ch02_p29_74.indd 66 10/27/10 2:30 PM programming exercises 67 • The product • The average p2.5 Write a program that prompts the user for two integers and then prints • The distance (absolute value of the difference) • The maximum (the larger of the two) • The minimum (the smaller of the two) Hint: The max and min functions are defined in the <algorithm> header. p2.6 Write a program that prompts the user for a measurement in meters and then converts it to miles, feet, and inches. p2.7 Write a program that prompts the user for a radius and then prints • The area and circumference of a circle with that radius • The volume and surface area of a sphere with that radius p2.8 Write a program that asks the user for the lengths of the sides of a rectangle and then prints • The area and perimeter of the rectangle • The length of the diagonal (use the Pythagorean theorem) p2.9 Improve the program discussed in the How To 2.1 to allow input of quarters in addition to bills. p2.10 Write a program that helps a person decide whether to buy a hybrid car. Your program’s inputs should be: • The cost of a new car • The estimated miles driven per year • The estimated gas price • The estimated resale value after 5 years Compute the total cost of owning the car for 5 years. (For simplicity, we will not take the cost of financing into account.) Obtain realistic prices for a new and used hybrid and a comparable car from the Web. Run your program twice, using today’s gas price and 15,000 miles per year. Include pseudocode and the program runs with your assignment. p2.11 The following pseudocode describes how a bookstore computes the price of an order from the total price and the number of the books that were ordered. Read the total book price and the number of books. Compute the tax (7.5% of the total book price). Compute the shipping charge ($2 per book). The price of the order is the sum of the total book price, the tax, and the shipping charge. Print the price of the order. Translate this pseudocode into a C++ program. cfe2_ch02_p29_74.indd 67 10/27/10 2:30 PM 68 Chapter 2 Fundamental data types p2.12 The following pseudocode describes how to turn a string containing a ten-digit phone number (such as "4155551212") into a more readable string with parentheses and dashes, like this: "(415) 555-1212". Take the substring consisting of the first three characters and surround it with "(" and ")". This is the area code. Concatenate the area code, the substring consisting of the next three characters, a hyphen, and the substring consisting of the last four characters. This is the formatted number. Translate this pseudocode into a C++ program that reads a telephone number into a string variable, computes the formatted number, and prints it. p2.13 The following pseudocode describes how to extract the dollars and cents from a price given as a floating-point value. For example, a price 2.95 yields values 2 and 95 for the dollars and cents. Assign the price to an integer variable dollars. Multiply the difference price - dollars by 100 and add 0.5. Assign the result to an integer variable cents. Translate this pseudocode into a C++ program. Read a price and print the dollars and cents. Test your program with inputs 2.95 and 4.35. p2.14 Giving change. Implement a program that directs a cashier how to give change. The program has two inputs: the amount due and the amount received from the customer. Display the dollars, quarters, dimes, nickels, and pennies that the customer should receive in return. p2.15 Write a program that asks the user to input • The number of gallons of gas in the tank • The fuel efficiency in miles per gallon • The price of gas per gallon Then print the cost per 100 miles and how far the car can go with the gas in the tank. p2.16 File names and extensions. Write a program that prompts the user for the drive letter (C), the path (\Windows\System), the file name (Readme), and the extension (txt). Then print the complete file name C:\Windows\System\Readme.txt. (If you use UNIX or a Macintosh, skip the drive name and use / instead of \ to separate directories.) p2.17 Write a program that reads a number between 1,000 and 999,999 from the user and prints it with a comma separating the thousands. Here is a sample dialog; the user input is in color: Please enter an integer between 1000 and 999999: 23456 23,456 p2.18 Write a program that reads a number between 1,000 and 999,999 from the user, where the user enters a comma in the input. Then print the number without a comma. Here is a sample dialog; the user input is in color: Please enter an integer between 1,000 and 999,999: 23,456 23456 Hint: Read the input as a string. Measure the length of the string. Suppose it contains n characters. Then extract substrings consisting of the first n – 4 characters and the last three characters. cfe2_ch02_p29_74.indd 68 10/27/10 2:30 PM programming exercises 69 p2.19 Printing a grid. Write a program that prints the following grid to play tic-tac-toe. +--+--+--+ | | | | +--+--+--+ | | | | +--+--+--+ | | | | +--+--+--+ Of course, you could simply write seven statements of the form cout << "+--+--+--+"; You should do it the smart way, though. Define string variables to hold two kinds of patterns: a comb-shaped pattern +--+--+--+ | | | | and the bottom line. Print the comb three times and the bottom line once. p2.20 Write a program that reads an integer and breaks it into a sequence of individual digits. For example, the input 16384 is displayed as 1 6 3 8 4 You may assume that the input has no more than five digits and is not negative. p2.21 Write a program that reads two times in military format (0900, 1730) and prints the number of hours and minutes between the two times. Here is a sample run. User input is in color. Please enter the first time: 0900 Please enter the second time: 1730 8 hours 30 minutes Extra credit if you can deal with the case where the first time is later than the second: Please enter the first time: 1730 Please enter the second time: 0900 15 hours 30 minutes p2.22 Writing large letters. A large letter H can be produced like this: * * * * ***** * * * * It can be defined as a string constant like this: const string LETTER_H = "* *\n* *\n*****\n* *\n* *\n"; (The \n character is explained in Special Topic 1.1.) Do the same for the letters E, L, and O. Then write the message H E L L O in large letters. cfe2_ch02_p29_74.indd 69 10/27/10 2:30 PM 70 Chapter 2 Fundamental data types p2.23 Write a program that transforms numbers 1, 2, 3, …, 12 into the corresponding month names January, February, March, …, December. Hint: Make a very long string "January February March ...", in which you add spaces such that each month name has the same length. Then use substr to extract the month you want. engineering p2.24 Consider the following circuit. R1 R2 R3 Write a program that reads the resistances of the three resistors and computes the total resistance, using Ohm’s law. engineering p2.25 The dew point temperature Td can be calculated (approximately) from the relative humidity RH and the actual temperature T by Td = b ⋅ f (T , RH ) a − f (T , RH ) f (T , RH ) = a ⋅T + ln ( RH ) b+T where a = 17.27 and b = 237.7° C. Write a program that reads the relative humidity (between 0 and 1) and the temperature (in degrees C) and prints the dew point value. Use the C++ function log to compute the natural logarithm. engineering p2.26 The pipe clip temperature sensors shown here are robust sensors that can be clipped directly onto copper pipes to measure the temperature of the liquids in the pipes. Each sensor contains a device called a thermistor. Thermistors are semiconductor devices that exhibit a temperature-dependent resistance described by: R = R0 e cfe2_ch02_p29_74.indd 70 1 1 β −   T T0  10/27/10 2:30 PM programming exercises 71 where R is the resistance (in Ω) at the temperature T (in °K), and R0 is the resistance (in Ω) at the temperature T0 (in °K). β is a constant that depends on the material used to make the thermistor. Thermistors are specified by providing values for R0, T0, and β. The thermistors used to make the pipe clip temperature sensors have R0 = 1075 Ω at T0 = 85 °C, and β = 3969 °K. (Notice that β has units of °K. Recall that the temperature in °K is obtained by adding 273 to the temperature in °C.) The liquid temperature, in °C, is determined from the resistance R, in Ω, using T = β T0 − 273  R T0 ln  +β  R0  Write a C++ program that prompts the user for the thermistor resistance R and prints a message giving the liquid temperature in °C. engineering p2.27 The circuit shown below illustrates some important aspects of the connection between a power company and one of its customers. The customer is represented by three parameters, Vt, P, and pf. Vt is the voltage accessed by plugging into a wall outlet. Customers depend on having a dependable value of Vt in order for their appliances to work properly. Accordingly, the power company regulates the value of Vt carefully. P describes the amount of power used by the customer and is the primary factor in determining the customer’s electric bill. The power factor, pf, is less familiar. (The power factor is calculated as the cosine of an angle so that its value will always be between zero and one.) In this problem you will be asked to write a C++ program to investigate the significance of the power factor. + – + R = 10 Ω Vs Vt = 120 Vrms R = 10 Ω Power Company – P = 260 W p f = 0.6 Customer Power Lines In the figure, the power lines are represented, somewhat simplistically, as resistances in Ohms. The power company is represented as an AC voltage source. The source voltage, Vs, required to provide the customer with power P at voltage Vt can be determined using the formula 2 Vs =   2RP  2RP   Vt + V  +  pf V    t  t 2 (1 − pf ) 2 (Vs has units of Vrms.) This formula indicates that the value of Vs depends on the value of pf. Write a C++ program that prompts the user for a power factor value and cfe2_ch02_p29_74.indd 71 10/27/10 2:30 PM 72 Chapter 2 Fundamental data types then prints a message giving the corresponding value of Vs, using the values for P, R, and Vt shown in the figure above. engineering p2.28 Consider the following tuning circuit connected to an antenna, where C is a variable capacitor whose capacitance ranges from Cmin to Cmax. Antenna L The tuning circuit selects the frequency f = C 2π . To design this circuit for a given LC frequency, take C = CminCmax and calculate the required inductance L from f and 2π to C. Now the circuit can be tuned to any frequency in the range fmin = LCmax 2π fmax = . LCmin Write a C++ program to design a tuning circuit for a given frequency, using a variable capacitor with given values for Cmin and Cmax. (A typical input is f = 16.7 MHz, Cmin = 14 pF, and Cmax = 365 pF.) The program should read in f (in Hz), Cmin and Cmax (in F), and print the required inductance value and the range of frequencies to which the circuit can be tuned by varying the capacitance. engineering p2.29 According to the Coulomb force law, the electric force between two charged par- ticles of charge Q1 and Q2 Coulombs, that are a distance r meters apart, is F = Q1 Q2 4π ε r2 Newtons, where ε = 8.854 × 10−12 Farads/meter. Write a program that calculates the force on a pair of charged particles, based on the user input of Q1 Coulombs, Q2 Coulombs, and r meters, and then computes and displays the electric force. cfe2_ch02_p29_74.indd 72 10/27/10 2:30 PM answers to self-Check Questions 73 ansWers to selF-CheCk QuestIons 1. One possible answer is int bottles_per_case = 8; You may choose a different variable name or a different initialization value, but your variable should have type int. 2. There are three errors: • You cannot have spaces in variable names. • The variable type should be double because it holds a fractional value. • There is a semicolon missing at the end of the statement. 3. double unit_price = 1.95; int quantity = 2; 4. cout << "Total price: " << unit_price * quantity << endl; 5. Change the definition of cans_per_pack to int cans_per_pack = 4; 6. You need to use a */ delimiter to close a comment that begins with a /*: double can_volume = 0.355; /* Liters in a 12-ounce can */ 7. The program would compile, and it would display the same result. However, a person reading the program might find it confusing that fractional cans are being considered. 8. Its value is modified by the assignment statement. 9. Assignment would occur when one car is replaced by another in the parking space. 10. double interest = balance * p / 100; 11. double side_length = sqrt(area); 12. 4 * PI * pow(radius, 3) / 3 or (4.0 / 3) * PI * pow(radius, 3), but not (4 / 3) * PI * pow(radius, 3) 13. 172 and 9 14. int leftover = 12 - amount % 12; 15. There is no prompt that alerts the program user to enter the quantity. 16. The unit_price variable is defined as an int. If the user were to enter a price such as 1.95, only the 1 would be placed into the variable. 17. The output is The total volume is20 Note that there is no space between the is and 20. 18. cout << "$" << fixed << setprecision(2) << total_price; 19. cout << "Bottles: " << setw(8) << bottles << endl << "Cans: " << setw(8) << cans << endl; Note that the setw manipulator appears twice. Also note the added spaces in the string "Cans: ". cfe2_ch02_p29_74.indd 73 10/27/10 2:30 PM 74 Chapter 2 Fundamental data types 20. int pairs = (total_width - tile_width) / (2 * tile_width); int tiles = 1 + 2 * pairs; double gap = (total_width - tiles * tile_width) / 2; 21. 22. 23. 24. Be sure that pairs is declared as an int. Now there are groups of four tiles (gray/white/gray/black) following the initial black tile. Therefore, the algorithm is now number of groups = integer part of (total width - tile width) / (4 x tile width) number of tiles = 1 + 4 x number of groups The formula for the gap is not changed. Clearly, the answer depends only on whether the row and column numbers are even or odd, so let’s first take the remainder after dividing by 2. Then we can enumerate all expected answers: Row % 2 Column % 2 Color 0 0 0 0 1 1 1 0 1 1 1 0 In the first three entries of the table, the color is simply the sum of the remainders. In the fourth entry, the sum would be 2, but we want a zero. We can achieve that by taking another remainder operation: color = ((row % 2) + (column % 2)) % 2 In nine years, the repair costs increased by $1,400. Therefore, the increase per year is $1,400 / 9 ≈ $156. The repair cost in year 3 would be $100 + 2 × $156 = $412. The repair cost in year n is $100 + n × $156. To avoid accumulation of roundoff errors, it is actually a good idea to use the original expression that yielded $156, that is, Repair cost in year n = 100 + n x 1400 / 9 The pseudocode follows easily from the equations. bottom volume = p x r12 x h1 top volume = p x r22 x h2 middle volume = p x (r12 + r1 x r2 + r22) x h3 / 3 total volume = bottom volume + top volume + middle volume Measuring a typical wine bottle yields r1 = 3.6, r2 = 1.2, h1 = 15, h2 = 7, h3 = 6 (all in centimeters). Therefore, bottom volume = 610.73 top volume = 31.67 middle volume = 135.72 total volume = 778.12 The actual volume is 750 ml, which is close enough to our computation to give confidence that it is correct. 25. The length is 11. The space counts as a character. 26. str.substr(7, 4) 27. str = str + "ming"; 28. Hy 29. cin >> first_name >> middle_initial >> last_name; cfe2_ch02_p29_74.indd 74 10/27/10 2:30 PM Chapter 3 DeCisions Chapter Goals to be able to implement decisions using if statements to learn how to compare integers, floating-point numbers, and strings to understand the Boolean data type to develop strategies for validating user input Chapter Contents 3.1 The if STaTemenT 76 Syntax 3.1: if statement 78 Programming Tip 3.1: Brace layout 79 Programming Tip 3.2: always Use Braces 80 Common Error 3.1: a semicolon after the if Condition 80 Programming Tip 3.3: tabs 81 Special Topic 3.1: the selection operator 81 Programming Tip 3.4: avoid Duplication in Branches 82 3.2 Comparing numberS and STringS 82 Syntax 3.2: Comparisons 83 Common Error 3.2: Confusing = and == 85 Programming Tip 3.5: Compile with Zero Warnings 85 Common Error 3.3: exact Comparison of Floating-point numbers 86 Special Topic 3.2: lexicographic ordering of strings 86 How To 3.1: implementing an if statement 87 Worked Example 3.1: extracting the Middle Random Fact 3.1: the Denver airport luggage handling system 89 3.3 mulTiple alTernaTiveS 90 Special Topic 3.3: the switch statement 93 3.4 neSTed branCheS 94 Programming Tip 3.6: hand-tracing 97 Common Error 3.4: the Dangling else problem 98 3.5 problem Solving: flowCharTS 3.6 problem Solving: TeST CaSeS 99 102 Programming Tip 3.7: Make a schedule and Make time for Unexpected problems 103 3.7 boolean variableS and operaTorS 103 Common Error 3.5: Combining Multiple relational operators 107 Common Error 3.6: Confusing && and || Conditions 107 Special Topic 3.4: short-Circuit evaluation of Boolean operators 108 Special Topic 3.5: De Morgan’s law 108 3.8 appliCaTion: inpuT validaTion 109 Random Fact 3.2: artificial intelligence 112 75 cfe2_ch03_p75_130.indd 75 10/28/10 7:51 PM one of the essential features of computer programs is their ability to make decisions. like a train that changes tracks depending on how the switches are set, a program can take different actions, depending on inputs and other circumstances. in this chapter, you will learn how to program simple and complex decisions. You will apply what you learn to the task of checking user input. 3.1 the if statement the if statement allows a program to carry out different actions depending on the nature of the data to be processed. The if statement is used to implement a decision. When a condition is fulfilled, one set of statements is executed. Otherwise, another set of statements is executed (see Syntax 3.1). Here is an example using the if statement. In many countries, the number 13 is considered unlucky. Rather than offending superstitious tenants, building owners sometimes skip the thirteenth floor; floor 12 is immediately followed by floor 14. Of course, floor 13 is not usually left empty or, as some conspiracy theorists believe, filled with secret offices and research labs. It is simply called floor 14. The computer that controls the building elevators needs to compensate for this foible and adjust all floor numbers above 13. Let’s simulate this process in C++. We will ask the user to type in the desired floor number and then compute the actual floor. When the input is above 13, then we need to decrement the input to This elevator panel “skips” the obtain the actual floor. For example, if the user thirteenth floor. The floor is not provides an input of 20, the program determines actually missing—the computer the actual floor as 19. Otherwise, we simply use the that controls the elevator adjusts the floor numbers above 13. supplied floor number. int actual_floor; if (floor > 13) { actual_floor = floor - 1; } else { actual_floor = floor; } The flowchart in Figure 1 shows the branching behavior. In our example, each branch of the if statement contains a single statement. You can include as many statements in each branch as you like. Sometimes, it happens that 76 cfe2_ch03_p75_130.indd 76 10/28/10 7:51 PM 3.1 the if statement 77 Condition No else branch True floor > 13? False actual_floor = floor actual_floor = floor - 1 True floor > 13? False actual_floor-- figure 1 figure 2 Flowchart for if statement Flowchart for if statement with no else Branch there is nothing to do in the else branch of the statement. In that case, you can omit it entirely, such as in this example: int actual_floor = floor; if (floor > 13) { actual_floor--; } // No else needed See Figure 2 for the flowchart. The following program puts the if statement to work. This program asks for the desired floor and then prints out the actual floor. An if statement is like a fork in the road. Depending upon a decision, different parts of the program are executed. cfe2_ch03_p75_130.indd 77 10/28/10 7:51 PM 78 Chapter 3 Decisions ch03/elevator1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> using namespace std; int main() { int floor; cout << "Floor: "; cin >> floor; int actual_floor; if (floor > 13) { actual_floor = floor - 1; } else { actual_floor = floor; } cout << "The elevator will travel to the actual floor " << actual_floor << endl; return 0; } program run Floor: 20 The elevator will travel to the actual floor 19 syntax 3.1 if statement Braces are not required if the branch contains a single statement, but it's good to always use them. See page 80. Omit the else branch if there is nothing to do. A condition that is true or false. Often uses relational operators: == != < <= > >= (See page 83.) if (floor > 13) { actual_floor = floor - 1; } else { actual_floor = floor; } Lining up braces is a good idea. See page 79. cfe2_ch03_p75_130.indd 78 Don’t put a semicolon here! See page 80. If the condition is true, the statement(s) in this branch are executed in sequence; if the condition is false, they are skipped. p If the condition is false, the statement(s) in this branch are executed in sequence; if the condition is true, they are skipped. 10/28/10 7:51 PM 3.1 1. Self CheCk 2. the if statement 79 In some Asian countries, the number 14 is considered unlucky. Some building owners play it safe and skip both the thirteenth and the fourteenth floor. How would you modify the sample program to handle such a building? Consider the following if statement to compute a discounted price: if (original_price > 100) { discounted_price = original_price - 20; } else { discounted_price = original_price - 10; } 3. What is the discounted price if the original price is 95? 100? 105? Compare this if statement with the one in Self Check 2: if (original_price < 100) { discounted_price = original_price - 10; } else { discounted_price = original_price - 20; } 4. Do the two statements always compute the same value? If not, when do the values differ? Consider the following statements to compute a discounted price: discounted_price = original_price; if (original_price > 100) { discounted_price = original_price - 10; } 5. practice it programming tip 3.1 What is the discounted price if the original price is 95? 100? 105? The variables fuel_amount and fuel_capacity hold the actual amount of fuel and the size of the fuel tank of a vehicle. If less than 10 percent is remaining in the tank, a status light should show a red color; otherwise it shows a green color. Simulate this process by printing out either "red" or "green". Now you can try these exercises at the end of the chapter: R3.3, R3.4, P3.16. brace layout Programmers vary in how they align braces in their code. In this book, we follow the simple rule of making { and } line up. if (floor > 13) { floor--; } This style makes it easy to spot matching braces. cfe2_ch03_p75_130.indd 79 Properly lining up your code makes your programs easier to read. 10/28/10 7:51 PM 80 Chapter 3 Decisions Some programmers put the opening brace on the same line as the if: if (floor > 13) { floor--; } p This style makes it harder to match the braces, but it saves a line of code, allowing you to view more code on the screen without scrolling. There are passionate advocates of both styles. It is important that you pick a layout style and stick with it consistently within a given programming project. Which style you choose may depend on your personal preference or a coding style guide that you need to follow. programming tip 3.2 always use braces When the body of an if statement consists of a single statement, you need not use braces. For example, the following is legal: if (floor > 13) floor--; However, it is a good idea to always include the braces: if (floor > 13) { floor--; } The braces makes your code easier to read, and you are less likely to make errors such as the one described in Common Error 3.1. Common error 3.1 a Semicolon after the if Condition The following code fragment has an unfortunate error: if (floor > 13) ; // { floor--; } ERROR There should be no semicolon after the if condition. The compiler interprets this statement as follows: If floor is greater than 13, execute the statement that is denoted by a single semicolon, that is, the do-nothing statement. The statement enclosed in braces is no longer a part of the if statement. It is always executed. Even if the value of floor is not above 13, it is decremented. Placing a semicolon after the else reserved word is also wrong: if (floor > 13) { actual_floor = floor - 1; } else ; { actual_floor = floor; } In this case, the do-nothing statement is executed if floor > 13 is not fulfilled. This is the end of the if statement. The next statement, enclosed in braces, is executed in both cases; that is, actual_floor is always set to floor. cfe2_ch03_p75_130.indd 80 10/28/10 7:51 PM 3.1 programming tip 3.3 the if statement 81 Tabs Block-structured code has the property that nested statements are indented by one or more levels: int main() { | int floor; | ... | if (floor > 13) | { | | floor--; | } | | ... | return 0; } | | 0 1 2 Indentation level You use How do you move the cursor from the leftmost the Tab key column to the appropriate indentation level? A to move the perfectly reasonable strategy is to hit the space bar cursor to the next a sufficient number of times. However, many proindentation level. grammers use the Tab key instead. A tab moves the cursor to the next indentation level. While the Tab key is nice, some editors use tab characters for alignment, which is not so nice. Tab characters can lead to problems when you send your file to another person or a printer. There is no universal agreement on the width of a tab character, and some software will ignore tabs altogether. It is therefore best to save your files with spaces instead of tabs. Most editors have a setting to automatically convert all tabs to spaces. Look at the documentation of your development environment to find out how to activate this useful setting. special topic 3.1 The Selection operator C++ has a selection operator of the form condition ? value1 : value2 The value of that expression is either value1 if the test passes or value2 if it fails. For example, we can compute the actual floor number as actual_floor = floor > 13 ? floor - 1 : floor; which is equivalent to if (floor > 13) { actual_floor = floor - 1; } else { actual_floor = floor; } You can use the selection operator anywhere that a value is expected, for example: cout << "Actual floor: " << (floor > 13 ? floor - 1 : floor); We don’t use the selection operator in this book, but it is a convenient construct that you will find in many C++ programs. cfe2_ch03_p75_130.indd 81 10/28/10 7:51 PM 82 Chapter 3 Decisions programming tip 3.4 avoid duplication in branches Look to see whether you duplicate code in each branch. If so, move it out of the if statement. Here is an example of such duplication: if (floor > 13) { actual_floor = floor - 1; cout << "Actual floor: " << actual_floor << endl; } else { actual_floor = floor; cout << "Actual floor: " << actual_floor << endl; } The output statement is exactly the same in both branches. This is not an error—the program will run correctly. However, you can simplify the program by moving the duplicated statement, like this: if (floor > 13) { actual_floor = floor - 1; } else { actual_floor = floor; } cout << "Actual floor: " << actual_floor << endl; Removing duplication is particularly important when programs are maintained for a long time. When there are two sets of statements with the same effect, it can easily happen that a programmer modifies one set but not the other. 3.2 Comparing numbers and strings relational operators (< <= > >= == !=) are used to compare numbers and strings. cfe2_ch03_p75_130.indd 82 Every if statement contains a condition. In many cases, the condition involves comparing two values. For example, in the previous examples we tested floor > 13. The comparison > is called a relational operator. C++ has six relational operators (see Table 1). As you can see, only two C++ relational operators (> and <) look as you would expect from the mathematical notation. Computer keyboards do not have keys for ≥, ≤, or ≠, but the >=, <=, and != operators are easy to remember because they look similar. The == operator is initially confusing to most newcomers to C++. In C++, = already has a meaning, namely assignment. In C++, you use a relational operator to check whether one value is greater than another. 10/28/10 7:51 PM 3.2 Comparing numbers and strings 83 table 1 relational operators C++ Math notation Description > > Greater than >= ≥ Greater than or equal < < Less than <= ≤ Less than or equal == = Equal != ≠ Not equal The == operator denotes equality testing: floor = 13; // Assign 13 to floor if (floor == 13) // Test whether floor equals 13 You must remember to use == inside tests and to use = outside tests. (See Common Error 3.2 on page 85 for more information.) You can compare strings as well: if (input == "Quit") ... Use != to check whether two strings are different. In C++, letter case matters. For example, "Quit" and "quit" are not the same string. syntax 3.2 Comparisons These quantities are compared. floor > 13 One of: == Check that you have the right direction: > (greater) or < (less) != < <= > >= (See page 83.) Check the boundary condition: Do you want to include (>=) or exclude (>)? floor == 13 Use ==, not =. See page 85. Checks for equality. string input; if (input == "Y") Ok to compare strings. (See page 86.) double x; double y; const double EPSILON = 1E-14; if (fabs(x - y) < EPSILON) Checks that these floating-point numbers are very close. See page 86. cfe2_ch03_p75_130.indd 83 10/28/10 7:51 PM 84 Chapter 3 Decisions table 2 relational operator examples expression Value Comment 3 <= 4 true 3 is less than 4; <= tests for “less than or equal”. 3 =< 4 error The “less than or equal” operator is <=, not =<. The “less than” symbol comes first. 3 > 4 false > is the opposite of <=. 4 < 4 false The left-hand side must be strictly smaller than the right-hand side. 4 <= 4 true Both sides are equal; 3 == 5 - 2 true == tests for equality. 3 != 5 - 1 true != tests for inequality. It is true that 3 is not 5 – 1. 3 = 6 / 2 error Use == to test for equality. 1.0 / 3.0 == 0.333333333 false Although the values are very close to one another, they are not exactly equal. See Common Error 3.3 on page 86. "10" > 5 error You cannot compare a string to a number. <= tests for “less than or equal”. Table 2 summarizes how to use relational operators in C++. Self CheCk 6. Which of the following conditions are true, provided a is 3 and b is 4? a. a + 1 <= b b. a + 1 >= b p c. a + 1 != b 7. Give the opposite of the condition floor > 13 8. What is the error in this statement? if (score_a = score_b) { cout << "Tie" << endl; } 9. Supply a condition in this if statement to test whether the user entered a Y: string input; cout << "Enter Y to quit." << endl; cin >> input; if (...) { cout << "Goodbye." << endl; return 0; } cfe2_ch03_p75_130.indd 84 10/28/10 7:51 PM 3.2 10. practice it Common error 3.2 Comparing numbers and strings 85 How do you test that a string str is not the empty string? Now you can try these exercises at the end of the chapter: R3.2, R3.5, P3.14. Confusing = and == The rule for the correct usage of = and == is very simple: In tests, always use == and never use =. If it is so simple, why can’t the compiler be helpful and flag any errors? Actually, the C++ language allows the use of = inside tests. To understand this, we have to go back in time. The creators of C, the predecessor to C++, were very frugal. They did not want to have special values true and false. Instead, they allowed any numeric value inside a condition, with the convention that 0 denotes false and any non-0 value denotes true. Furthermore, in C and C++ assignments have values. For example, the value of the assignment expression floor = 13 is 13. These two features—namely that numbers can be used as truth values and that assignments are expressions with values—conspire to make a horrible pitfall. The test if (floor = 13) // ERROR is legal C++, but it does not test whether floor and 13 are equal. Instead, the code sets floor to 13, and since that value is not zero, the condition of the if statement is always fulfilled. Fortunately, most compilers issue a warning when they encounter such a statement. You should take such warnings seriously. (See Programming Tip 3.5 for more advice about compiler warnings.) Some shell-shocked programmers are so nervous about using = that they use == even when they want to make an assignment: floor == floor - 1; // ERROR This statement tests whether floor equals floor - 1. It doesn’t do anything with the outcome of the test, but that is not an error. Some compilers will warn that “the code has no effect”, but others will quietly accept the code. programming tip 3.5 Compile with Zero warnings There are two kinds of messages that the compiler gives you: errors and warnings. Error messages are fatal; the compiler will not translate a program with one or more errors. Warning messages are advisory; the compiler will translate the program, but there is a good chance that the program will not do what you expect it to do. It is a good idea to learn how to activate warnings with your compiler, and to write code that emits no warnings at all. For example, consider the test if (floor = 13) One C++ compiler emits a curious warning message: “Suggest parentheses around assignment used as truth value”. Sadly, the message is misleading because it was not written for students. Nevertheless, such a warning gives you another chance to look at the offending statement and fix it, in this case, by replacing the = with an ==. In order to make warnings more visible, many compilers require you to take some special action. This might involve clicking a checkbox in an integrated environment or supplying a special option on the command line. Ask your instructor or lab assistant how to turn on warnings for your compiler. cfe2_ch03_p75_130.indd 85 10/28/10 7:51 PM 86 Chapter 3 Decisions Common error 3.3 exact Comparison of floating-point numbers Floating-point numbers have only a limited precision, and calculations can introduce roundoff errors. You must take these inevitable roundoffs into account when comparing floating-point numbers. For example, the following code multiplies the square root of 2 by itself. Ideally, we expect to get the answer 2: double r = sqrt(2.0); if (r * r == 2) { cout << "sqrt(2) squared is 2" << endl; } else { cout << "sqrt(2) squared is not 2 but " << setprecision(18) << r * r << endl; } Take limited precision into account when comparing floating­point numbers. This program displays sqrt(2) squared is not 2 but 2.00000000000000044 It does not make sense in most circumstances to compare floating-point numbers exactly. Instead, we should test whether they are close enough. That is, the magnitude of their difference should be less than some threshold. Mathematically, we would write that x and y are close enough if x−y <ε for a very small number, e. e is the Greek letter epsilon, a letter used to denote a very small quantity. It is common to set e to 10–14 when comparing double numbers: const double EPSILON = 1E-14; double r = sqrt(2.0); if (fabs(r * r - 2) < EPSILON) { cout << "sqrt(2) squared is approximately 2"; } Include the <cmath> header to use the fabs function. special topic 3.2 lexicographic ordering of Strings If you compare strings using < <= > >=, they are compared in “lexicographic” order. This ordering is very similar to the way in which words are sorted in a dictionary. For example, consider this code fragment. string name = "Tom"; if (name < "Dick") ... The condition is not fulfilled, because in the dictionary Dick comes before Tom. There are a few differences between the ordering in a dictionary and in C++. In C++: • All uppercase letters come before the lowercase letters. For example, "Z" comes before "a". cfe2_ch03_p75_130.indd 86 To see which of two terms comes first in the dictionary, consider the first letter in which they differ. 10/28/10 7:51 PM 3.2 Comparing numbers and strings • The space character comes before all printable characters. • Numbers come before letters. • For the ordering of punctuation marks, see Appendix D. When comparing two strings, you compare the first letters of each word, then the second letters, and so on, until one of the strings ends or you find the first letter pair that doesn’t match. If one of the strings ends, the longer string is considered the “larger” one. For example, compare "car" with "cart". The first three letters match, and we reach the end of the first string. Therefore "car" comes before "cart" in lexicographic ordering. When you reach a mismatch, the string containing the “larger” character is considered “larger”. For example, let’s compare "cat" with "cart". The first two letters match. Since t comes after r, the string "cat" comes after "cart" in the lexicographic ordering. hoW to 3.1 87 lexicographic order is used to compare strings. c a r c a r c a t t Letters r comes match before t Lexicographic Ordering implementing an if Statement This How To walks you through the process of implementing an if statement. We will illustrate the steps with the following example problem: The university bookstore has a Kilobyte Day sale every October 24, giving an 8 percent discount on all computer accessory purchases if the price is less than $128, and a 16 percent discount if the price is at least $128. Write a program that asks the cashier for the original price and then prints the discounted price. Step 1 Decide upon the branching condition. In our sample problem, the obvious choice for the condition is: original price < 128? That is just fine, and we will use that condition in our solution. But you could equally well come up with a correct solution if you choose the opposite condition: Is the original price at least (≥) $128? You might choose this condition if you put yourself into the position of a shopper who wants to know when the bigger discount applies. Step 2 Sales discounts are often higher for expensive products. Use the if state­ ment to implement such a decision. Give pseudocode for the work that needs to be done when the condition is true. In this step, you list the action or actions that are taken in the “positive” branch. The details depend on your problem. You may want to print a message, compute values, or even exit the program. In our example, we need to apply an 8 percent discount: discounted price = 0.92 x original price cfe2_ch03_p75_130.indd 87 10/28/10 7:51 PM 88 Chapter 3 Decisions Step 3 Give pseudocode for the work (if any) that needs to be done when the condition is not true. What do you want to do in the case that the condition of Step 1 is not fulfilled? Sometimes, you want to do nothing at all. In that case, use an if statement without an else branch. In our example, the condition tested whether the price was less than $128. If that condition is not true, the price is at least $128, so the higher discount of 16 percent applies to the sale: discounted price = 0.84 x original price Step 4 Double-check relational operators. First, be sure that the test goes in the right direction. It is a common error to confuse > and <. Next, consider whether you should use the < operator or its close cousin, the <= operator. What should happen if the original price is exactly $128? Reading the problem carefully, we find that the lower discount applies if the original price is less than $128, and the higher discount applies when it is at least $128. A price of $128 should therefore not fulfill our condition, and we must use <, not <=. Step 5 Remove duplication. Check which actions are common to both branches, and move them outside. (See Programming Tip 3.4 on page 82.) In our example, we have two statements of the form discounted price = ___ x original price They only differ in the discount rate. It is best to just set the rate in the branches, and to do the computation afterwards: If original price < 128 discount rate = 0.92 Else discount rate = 0.84 discounted price = discount rate x original price Step 6 Test both branches. Formulate two test cases, one that fulfills the condition of the if statement, and one that does not. Ask yourself what should happen in each case. Then follow the pseudocode and act each of them out. In our example, let us consider two scenarios for the original price: $100 and $200. We expect that the first price is discounted by $8, the second by $32. When the original price is 100, then the condition 100 < 128 is true, and we get discount rate = 0.92 discounted price = 0.92 x 100 = 92 When the original price is 200, then the condition 200 < 128 is false, and discount rate = 0.84 discounted price = 0.84 x 200 = 168 In both cases, we get the expected answer. Step 7 Assemble the if statement in C++. Type the skeleton if () { } else { } cfe2_ch03_p75_130.indd 88 10/28/10 7:51 PM 3.2 Comparing numbers and strings 89 and fill it in, as shown in Syntax 3.1 on page 78. Omit the else branch if it is not needed. In our example, the completed statement is if (original_price < 128) { discount_rate = 0.92; } else { discount_rate = 0.84; } discounted_price = discount_rate * original_price; WorkeD exaMple 3.1 extracting the middle This Worked Example shows how to extract the middle character from a string, or the two middle characters if the length of the string is even. Random Fact 3.1 Making decisions is an essential part of any computer program. nowhere is this more obvious than in a computer system that helps sort luggage at an airport. after scanning the luggage identification codes, the system sorts the items and routes them to different conveyor belts. human operators then place the items onto trucks. When the city of Denver built a huge airport to replace an outdated and congested facility, the luggage system contractor went a step further. the new system was designed to replace the human operators with robotic carts. Unfortunately, the system plainly did not work. it was plagued by mechanical problems, such as luggage falling onto the tracks and jamming carts. equally frustrating were the software glitches. Carts would uselessly accumulate at some locations when they were needed elsewhere. c r a t e 0 1 2 3 4 the Denver airport luggage handling system the airport had been scheduled to open in 1993, but without a functioning luggage system, the opening was delayed for over a year while the contractor tried to fix the problems. the contractor never succeeded, and ultimately a manual system was installed. the delay cost the city and airlines close to a billion dollars, and the contractor, once the leading luggage systems vendor in the United states, went bankrupt. Clearly, it is very risky to build a large system based on a technology that has never been tried on a smaller scale. as robots and the software that controls them get better over time, they will take on a larger share of luggage handling in the future. But it is likely that this will happen in an incremental fashion. The Denver airport originally had a fully automatic system for moving lug­ gage, replacing human operators with robotic carts. Unfortunately, the sys­ tem never worked and was dismantled before the airport was opened. Available online at www.wiley.com/college/horstmann. cfe2_ch03_p75_130.indd 89 10/28/10 7:51 PM 90 Chapter 3 Decisions 3.3 Multiple alternatives Multiple alternatives are required for decisions that have more than two cases. The 1989 Loma Prieta earthquake that damaged the Bay Bridge in San Fran­ cisco and destroyed many buildings measured 7.1 on the Richter scale. In Section 3.1, you saw how to program a two-way branch with an if statement. In many situations, there are more than two cases. In this section, you will see how to implement a decision with multiple alternatives. For example, consider a program that displays the effect of an earthquake, as measured by the Richter scale (see Table 3). table 3 richter scale Value effect 8 Most structures fall 7 Many buildings destroyed 6 Many buildings considerably damaged, some collapse 4.5 Damage to poorly constructed buildings The Richter scale is a measurement of the strength of an earthquake. Every step in the scale, for example from 6.0 to 7.0, signifies a tenfold increase in the strength of the quake. In this case, there are five branches: one each for the four descriptions of damage, and one for no destruction. Figure 3 shows the flowchart for this multiple-branch statement. You use multiple if statements to implement multiple alternatives, like this: if (richter >= 8.0) { cout << "Most structures fall"; } else if (richter >= 7.0) { cout << "Many buildings destroyed"; } else if (richter >= 6.0) { cout << "Many buildings considerably damaged, some collapse"; } else if (richter >= 4.5) { cout << "Damage to poorly constructed buildings"; } else { cout << "No destruction of buildings"; } As soon as one of the four tests succeeds, the effect is displayed, and no further tests are attempted. If none of the four cases applies, the final else clause applies, and a default message is printed. (See the ch03/richter.cpp file for the full program.) cfe2_ch03_p75_130.indd 90 10/28/10 7:51 PM 3.3 Multiple alternatives 91 figure 3 Multiple alternatives richter ≥ 8.0? True Most structures fall False richter ≥ 7.0? True Many buildings destroyed False richter ≥ 6.0? True Many buildings considerably damaged, some collapse False richter ≥ 4.5? True Damage to poorly constructed buildings False No destruction of buildings Here you must sort the conditions and test against the largest cutoff first. Suppose we reverse the order of tests: if (richter >= 4.5) // Tests in wrong order { cout << "Damage to poorly constructed buildings"; } else if (richter >= 6.0) { cout << "Many buildings considerably damaged, some collapse"; } else if (richter >= 7.0) { cout << "Many buildings destroyed"; } else if (richter >= 8.0) { cfe2_ch03_p75_130.indd 91 10/28/10 7:51 PM 92 Chapter 3 Decisions cout << "Most structures fall"; } When using multiple if statements, pay attention to the order of the conditions. This does not work. Suppose the value of richter is 7.1. That value is at least 4.5, matching the first case. The other tests will never be attempted. In this example, it is also important that we use a sequence of else if clauses, not just multiple independent if statements. Consider this sequence of independent tests: if (richter >= 8.0) // Didn’t use else { cout << "Most structures fall"; } if (richter >= 7.0) { cout << "Many buildings destroyed"; } if (richter >= 6.0) { cout << "Many buildings considerably damaged, some collapse"; } if (richter >= 4.5) { cout << "Damage to poorly constructed buildings"; } Now the alternatives are no longer exclusive. If richter is 7.1, then the last three tests all match, and three messages are printed. Self CheCk 11. 12. 13. 14. In a game program, the scores of players A and B are stored in variables score_a and score_b. Assuming that the player with the larger score wins, write a sequence of conditional statements that prints out "A won", "B won", or "Game tied". Write a conditional statement with three branches that sets s to 1 if x is positive, to –1 if x is negative, and to 0 if x is zero. How could you achieve the task of Self Check 12 with only two branches? Beginners sometimes write statements such as the following: if (price > 100) { discounted_price = price - 20; } else if (price <= 100) { discounted_price = price - 10; } 15. 16. practice it cfe2_ch03_p75_130.indd 92 Explain how this code can be improved. Suppose the user enters -1 into the richter.cpp program. What is printed? Suppose we want to have the richter.cpp program check whether the user entered a negative number. What branch would you add to the if statement, and where? Now you can try these exercises at the end of the chapter: R3.20, P3.1, P3.9, P3.10. 10/28/10 7:51 PM 3.3 special topic 3.3 Multiple alternatives 93 The switch Statement A sequence of if statements that compares a single integer value against several constant alternatives can be implemented as a switch statement. For example, int digit; ... switch (digit) { case 1: digit_name = "one"; break; case 2: digit_name = "two"; break; case 3: digit_name = "three"; break; case 4: digit_name = "four"; break; case 5: digit_name = "five"; break; case 6: digit_name = "six"; break; case 7: digit_name = "seven"; break; case 8: digit_name = "eight"; break; case 9: digit_name = "nine"; break; default: digit_name = ""; break; } This is a shortcut for int digit; if (digit == 1) { else if (digit == else if (digit == else if (digit == else if (digit == else if (digit == else if (digit == else if (digit == else if (digit == else { digit_name digit_name = "one"; } 2) { digit_name = "two"; } 3) { digit_name = "three"; } 4) { digit_name = "four"; } 5) { digit_name = "five"; } 6) { digit_name = "six"; } 7) { digit_name = "seven"; } 8) { digit_name = "eight"; } 9) { digit_name = "nine"; } = ""; } Well, it isn’t much of a shortcut, but it has one advantage—it is obvious that all branches test the same value, namely digit. It is possible to have multiple case clauses for a branch, such as case 1: case 3: case 5: case 7: case 9: odd = true; break; The default branch is chosen if none of the case clauses match. Every branch of the switch must be terminated by a break instruction. If the break is missing, execution falls The switch statement lets you through to the next branch, and so on, until finally a choose from a fixed set of break or the end of the switch is reached. In practice, this alternatives. fall-through behavior is rarely useful, but it is a common cause of errors. If you accidentally forget the break statement, your program compiles but executes unwanted code. Many programmers consider the switch statement somewhat dangerous and prefer the if statement. We leave it to you to use the switch statement for your own code or not. At any rate, you need to have a reading knowledge of switch in case you find it in other programmers’ code. cfe2_ch03_p75_130.indd 93 10/28/10 7:51 PM 94 Chapter 3 Decisions 3.4 nested Branches When a decision statement is contained inside the branch of another decision statement, the statements are nested. nested decisions are required for problems that have two levels of decision making. It is often necessary to include an if statement inside another. Such an arrangement is called a nested set of statements. Here is a typical example. In the United States, different tax rates are used depending on the taxpayer’s marital status. There are different tax schedules for single and for married taxpayers. Married taxpayers add their income together and pay taxes on the total. Table 4 gives the tax rate computations, using a simplification of the schedules in effect for the 2008 tax year. A different tax rate applies to each “bracket”. In this schedule, the income at the first bracket is taxed at 10 percent, and the income at the second bracket is taxed at 25 percent. The income limits for each bracket depend on the marital status. Now compute the taxes due, given a filing status and an income figure. The key point is that there are two levels of decision making. First, you must branch on the marital status. Then, for each filing status, you must have another branch on income level. table 4 Federal tax rate schedule if your status is single and if the taxable income is the tax is of the amount over at most $32,000 10% $0 over $32,000 $3,200 + 25% $32,000 if your status is Married and if the taxable income is the tax is of the amount over at most $64,000 10% $0 over $64,000 $6,400 + 25% $64,000 The two-level decision process is reflected in two levels of if statements in the program at the end of this section. (See Figure 4 for a flowchart.) Computing income taxes requires multiple levels of decisions. cfe2_ch03_p75_130.indd 94 10/28/10 7:51 PM 3.4 True Single income True ≤ 32,000 False nested Branches 95 10% bracket False 25% bracket income True ≤ 64,000 10% bracket False 25% bracket figure 4 income tax Computation In theory, nesting can go deeper than two levels. A three-level decision process (first by state, then by filing status, then by income level) requires three nesting levels. ch03/tax.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 cfe2_ch03_p75_130.indd 95 #include <iostream> #include <string> using namespace std; int main() { const double const double const double const double RATE1 = 0.10; RATE2 = 0.25; RATE1_SINGLE_LIMIT = 32000; RATE1_MARRIED_LIMIT = 64000; double tax1 = 0; double tax2 = 0; double income; cout << "Please enter your income: "; cin >> income; cout << "Please enter s for single, m for married: "; string marital_status; cin >> marital_status; 10/28/10 7:51 PM 96 Chapter 3 Decisions 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 if (marital_status == "s") { if (income <= RATE1_SINGLE_LIMIT) { tax1 = RATE1 * income; } else { tax1 = RATE1 * RATE1_SINGLE_LIMIT; tax2 = RATE2 * (income - RATE1_SINGLE_LIMIT); } } else { if (income <= RATE1_MARRIED_LIMIT) { tax1 = RATE1 * income; } else { tax1 = RATE1 * RATE1_MARRIED_LIMIT; tax2 = RATE2 * (income - RATE1_MARRIED_LIMIT); } } p double total_tax = tax1 + tax2; cout << "The tax is $" << total_tax << endl; return 0; } program run Please enter your income: 80000 Please enter s for single, m for married: m The tax is $10400 Self CheCk 17. 18. What is the amount of tax that a single taxpayer pays on an income of $32,000? Would that amount change if the first nested if statement changed from if (income <= RATE1_SINGLE_LIMIT) to if (income < RATE1_SINGLE_LIMIT) 19. 20. 21. practice it cfe2_ch03_p75_130.indd 96 Suppose Harry and Sally each make $40,000 per year. Would they save taxes if they married? How would you modify the tax.cpp program in order to check that the user entered a correct value for the marital status (i.e., s or m)? Some people object to higher tax rates for higher incomes, claiming that you might end up with less money after taxes when you get a raise for working hard. What is the flaw in this argument? Now you can try these exercises at the end of the chapter: R3.7, R3.19, P3.13, P3.19. 10/28/10 7:51 PM 3.4 programming tip 3.6 nested Branches 97 hand-Tracing A very useful technique for understanding whether a program works correctly is called hand-tracing. You simulate the program’s activity on a sheet of paper. You can use this method with pseudocode or C++ code. Get an index card, a cocktail napkin, or whatever sheet of paper is within reach. Make a column for each variable. Have the program code ready. Use a marker, such as a paper clip, to mark the current statement. In your mind, execute statements one at a time. Every time the value of a variable changes, cross out the old value and write the new value below the old one. For example, let’s trace the tax program with the data from the program run on page 95. In lines 13 and 14, tax1 and tax2 are initialized to 0. 6 int main() 7 { 8 const double RATE1 = 0.10; 9 const double RATE2 = 0.25; 10 const double RATE1_SINGLE_LIMIT = 32000; 11 const double RATE1_MARRIED_LIMIT = 64000; 12 double tax1 = 0; 13 14 double tax2 = 0; 15 Hand­tracing helps you understand whether a program works correctly. income marital status tax1 tax2 0 0 tax1 tax2 income marital status 0 0 80000 m In lines 18 and 22, income and marital_status are initialized by input statements. 16 17 18 19 20 21 22 23 double income; cout << "Please enter your income: "; cin >> income; cout << "Please enter s for single, m for married: "; string marital_status; cin >> marital_status; Because marital_status is not "s", we move to the else branch of the outer if statement (line 36). 24 25 26 27 28 29 30 31 32 33 34 35 36 37 if (marital_status == "s") { if (income <= RATE1_SINGLE_LIMIT) { tax1 = RATE1 * income; } else { tax1 = RATE1 * RATE1_SINGLE_LIMIT; tax2 = RATE2 * (income - RATE1_SINGLE_LIMIT); } } else { Since income is not <= 38 39 40 41 42 43 44 45 46 cfe2_ch03_p75_130.indd 97 64000, we move to the else branch of the inner if statement (line 42). if (income <= RATE1_MARRIED_LIMIT) { tax1 = RATE1 * income; } else { tax1 = RATE1 * RATE1_MARRIED_LIMIT; tax2 = RATE2 * (income - RATE1_MARRIED_LIMIT); } 10/28/10 7:51 PM 98 Chapter 3 Decisions The values of tax1 and tax2 are updated. 43 44 45 46 47 { } } tax1 = RATE1 * RATE1_MARRIED_LIMIT; tax2 = RATE2 * (income - RATE1_MARRIED_LIMIT); tax1 tax2 income marital status 0 0 80000 m 6400 4000 Their sum total_tax is computed and printed. Then the program ends. 48 49 50 51 52 53 } cout << "The tax is $" << total_tax << endl; return 0; Because the program trace shows the expected output ($10,400), it successfully demonstrated that this test case works correctly. Common error 3.4 tax1 tax2 income marital status 0 0 80000 m 6400 4000 double total_tax = tax1 + tax2; total tax 10400 The dangling else problem When an if statement is nested inside another if statement, the following error may occur. double shipping_charge = 5.00; // $5 inside continental U.S. if (country == "USA") if (state == "HI") shipping_charge = 10.00; // Hawaii is more expensive else // Pitfall! shipping_charge = 20.00; // As are foreign shipments The indentation level seems to suggest that the else is grouped with the test country == "USA". Unfortunately, that is not the case. The compiler ignores all indentation and matches the else with the preceding if. That is, the code is actually double shipping_charge = 5.00; // if (country == "USA") if (state == "HI") shipping_charge = 10.00; // else // Pitfall! shipping_charge = 20.00; // $5 inside continental U.S. Hawaii is more expensive As are foreign shipments That isn’t what you want. You want to group the else with the first if. The ambiguous else is called a dangling else. You can avoid this pitfall if you always use braces, as recommended in Programming Tip 3.2 on page 80: double shipping_charge = 5.00; // $5 inside continental U.S. if (country == "USA") { if (state == "HI") { shipping_charge = 10.00; // Hawaii is more expensive } } else { shipping_charge = 20.00; // As are foreign shipments } cfe2_ch03_p75_130.indd 98 10/28/10 7:51 PM 3.5 problem solving: Flowcharts 99 3.5 problem solving: Flowcharts Flow charts are made up of elements for tasks, input/outputs, and decisions. You have seen examples of flowcharts earlier in this chapter. A flowchart shows the structure of decisions and tasks that are required to solve a problem. When you have to solve a complex problem, it is a good idea to draw a flowchart to visualize the flow of control. The basic flowchart elements are shown in Figure 5. Simple task figure 5 Input/output Condition True Flowchart elements False each branch of a decision can contain tasks and further decisions. The basic idea is simple enough. Link tasks and input/output boxes in the sequence in which they should be executed. Whenever you need to make a decision, draw a diamond with two outcomes (see Figure 6). Each branch can contain a sequence of tasks and even additional decisions. If there are multiple choices for a value, lay them out as in Figure 7. Choice 1 True “Choice 1” branch True “Choice 2” branch True “Choice 3” branch False Choice 2 False Condition True Choice 3 False False False branch figure 6 cfe2_ch03_p75_130.indd 99 True branch Flowchart with two outcomes “Other” branch figure 7 Flowchart with Multiple Choices 10/28/10 7:51 PM 100 Chapter 3 Decisions never point an arrow inside another branch. There is one issue that you need to be aware of when drawing flowcharts. Unconstrained branching and merging can lead to “spaghetti code”, a messy network of possible pathways through a program. There is a simple rule for avoiding spaghetti code: Never point an arrow inside another branch. To understand the rule, consider this example: Shipping costs are $5 inside the United States, except that to Hawaii and Alaska they are $10. International shipping costs are also $10. You might start out with a flowchart like the following: Inside US? Spaghetti code has so many pathways that it becomes impossible to understand. True False Shipping cost = $10 Continental US? True False Shipping cost = $5 Now you may be tempted to reuse the “shipping cost = $10” task: Inside US? True False Shipping cost = $10 Continental US? True False Shipping cost = $5 cfe2_ch03_p75_130.indd 100 10/28/10 7:51 PM 3.5 problem solving: Flowcharts 101 Don’t do that! The red arrow points inside a different branch. Instead, add another task that sets the shipping cost to $10, like this: Inside US? True False Shipping cost = $10 Continental US? True False Shipping cost = $10 Shipping cost = $5 Not only do you avoid spaghetti code, but it is also a better design. In the future it may well happen that the cost for international shipments is different from that to Alaska and Hawaii. Flowcharts can be very useful for getting an intuitive understanding of the flow of an algorithm. However, they get large rather quickly when you add more details. At that point, it makes sense to switch from flowcharts to pseudocode. Self CheCk 22. 23. 24. 25. 26. practice it cfe2_ch03_p75_130.indd 101 Draw a flowchart for a program that reads a value temp and prints “Frozen” if it is less than zero. What is wrong with the flowchart at right? How do you fix the flowchart of True Input < 0? Self Check 23? Draw a flowchart for a program that reads a False value x. If it is less than zero, print “Error”. Otherwise, print its square root. True Draw a flowchart for a program that reads Input > 100? a value temp. If it is less than zero, print “Ice”. If it is greater than 100, print “Steam”. False Otherwise, print “Liquid”. Now you can try these exercises at the end of the chapter: R3.10, R3.11, R3.12. Status = “OK” Status = “Error” 10/28/10 7:51 PM 102 Chapter 3 Decisions 3.6 problem solving: test Cases each branch of your program should be covered by a test case. p Consider how to test the tax computation program from Section 3.4. Of course, you cannot try out all possible inputs of filing status and income level. Even if you could, there would be no point in trying them all. If the program correctly computes one or two tax amounts in a given bracket, then we have a good reason to believe that all amounts will be correct. You want to aim for complete coverage of all decision points. Here is a plan for obtaining a comprehensive set of test cases: • There are two possibilities for the filing status and two tax brackets for each status, yielding four test cases. • Test a handful of boundary conditions, such as an income that is at the boundary between two brackets, and a zero income. • If you are responsible for error checking (which is discussed in Section 3.8), also test an invalid input, such as a negative income. Make a list of the test cases and the expected outputs: Test Case 30,000 72,000 50,000 104,000 32,000 0 it is a good idea to design test cases before implementing a program. Self CheCk 27. 29. 30. cfe2_ch03_p75_130.indd 102 Expected Output 3,000 13,200 5,000 16,400 3,200 0 Comment 10% bracket 3,200 + 25% of 40,000 10% bracket 6,400 + 25% of 40,000 boundary case boundary case When you develop a set of test cases, it is helpful to have a flowchart of your program (see Section 3.5). Check off each branch that has a test case. Include test cases for the boundary cases of each decision. For example, if a decision checks whether an input is less than 100, test with an input of 100. It is always a good idea to design test cases before starting to code. Working through the test cases gives you a better understanding of the algorithm that you are about to implement. 28. practice it s s m m s Using Figure 1 on page 77 as a guide, follow the process described in Section 3.6 to design a set of test cases for the elevator.cpp program in Section 3.1. What is a boundary test case for the algorithm in How To 3.1 on page 87? What is the expected output? Using Figure 3 on page 91 as a guide, follow the process described in Section 3.6 to design a set of test cases for the richter.cpp program in Section 3.3. Suppose you are designing a part of a program for a medical robot that has a sensor returning an x and y location (measured in cm). You need to check whether the sensor location is inside the circle, outside the circle, on the boundary (specifically, having a distance of less than 1 mm from the boundary). Assume the circle has center (0, 0) and radius 2 cm. Give a set of test cases. Now you can try these exercises at the end of the chapter: R3.13, R3.14. 10/28/10 7:51 PM 3.7 programming tip 3.7 Boolean Variables and operators 103 make a Schedule and make Time for unexpected problems Commercial software is notorious for being delivered later than promised. For example, Microsoft originally promised that its Windows Vista operating system would be available late in 2003, then in 2005, then in March 2006; it finally was released in January 2007. Some of the early promises might not have been realistic. It was in Microsoft’s interest to let prospective customers expect the imminent availability of the product. Had customers known the actual delivery date, they might have switched to a different product in the meantime. Undeniably, though, Microsoft had not anticipated the full complexity of the tasks it had set itself to solve. Microsoft can delay the delivery of its product, but it is likely that you cannot. As a student or a programmer, you are expected to manage your time wisely and to finish your assignments on time. You can probably do simple programming exercises the night before the due date, but an assignment that looks twice as hard may well take four times as long, because more things can go wrong. You should therefore make a schedule whenever you start a programming project. First, estimate realistically how much time it will take you to: • Design the program logic. • Develop test cases. • Type in the program and fix syntax errors. • Test and debug the program. For example, for the income tax program I might estimate an hour for the design; 30 minutes for developing test cases; an hour for data entry and fixing syntax errors; and an hour for testing and debugging. That is a total of 3.5 hours. If I work two hours a day on this project, it will take me almost two days. Then think of things that can go wrong. Your computer might break down. You might be Make a schedule for your programming stumped by a problem with the computer sys- work and build in time for problems. tem. (That is a particularly important concern for beginners. It is very common to lose a day over a trivial problem just because it takes time to track down a person who knows the magic command to overcome it.) As a rule of thumb, double the time of your estimate. That is, you should start four days, not two days, before the due date. If nothing went wrong, great; you have the program done two days early. When the inevitable problem occurs, you have a cushion of time that protects you from embarrassment and failure. 3.7 Boolean Variables and operators the Boolean type bool has two values, false and true. cfe2_ch03_p75_130.indd 103 Sometimes, you need to evaluate a logical condition in one part of a program and use it elsewhere. To store a condition that can be true or false, you use a Boolean variable. Boolean variables are named after the mathematician George Boole (1815–1864), a pioneer in the study of logic. In C++, the bool data type represents the Boolean type. Variables of type bool can hold exactly two values, denoted false and true. These values are not strings or integers; they are special values, just for Boolean variables. 10/28/10 7:51 PM 104 Chapter 3 Decisions Here is a definition of a Boolean variable: bool failed = true; You can use the value later in your program to make a decision: if (failed) // Only executed if failed has been set to true { ... } A Boolean variable is also called a flag because it can be either up (true) or down (false). When you make complex decisions, you often need to combine Boolean values. An operator that combines Boolean conditions is called a Boolean operator. In C++, the && operator (called and) yields true only when both conditions are true. The || operator (called or) yields the result true if at least one of the conditions is true. Suppose you write a program that processes temperature values, and you want to test whether a given temperature corresponds to liquid water. (At sea level, water freezes at 0 degrees Celsius and boils at 100 degrees.) Water is liquid if the temperature is greater than zero and less than 100: if (temp > 0 && temp < 100) { cout << "Liquid"; } C++ has two Boolean operators that combine conditions: && (and) and || (or). The condition of the test has two parts, joined by the && operator. (As shown in Table 5 and Appendix C, the > and < operators have higher precedence than the && operator.) Each part is a Boolean value that can be true or false. The combined expression is true if both individual expressions are true. If either one of the expressions is false, then the result is also false (see Figure 8). a B a && B a B a || B a !a true true true true true true true false true false false true false true false true false true false false true true false false false false false false figure 8 Boolean truth tables At this geyser in Iceland, you can see ice, liquid water, and steam. cfe2_ch03_p75_130.indd 104 10/28/10 7:51 PM 3.7 Boolean Variables and operators 105 table 5 selected operators and their precedence operator ++ -- + (unary) – (unary) ! * / % + < <= > >= == != Description Increment, decrement, positive, negative, Boolean not Multiplication, division, remainder Addition, subtraction Comparisons Equal, not equal && Boolean and || Boolean or Conversely, let’s test whether water is not liquid at a given temperature. That is the case when the temperature is at most 0 or at least 100. Use the || (or) operator to combine the expressions: if (temp <= 0 || temp >= 100) { cout << "Not liquid"; } to invert a condition, use the ! (not) operator. Figure 9 shows flowcharts for these examples. Sometimes you need to invert a condition with the not logical operator. The ! operator takes a single condition and evaluates to true if that condition is false and to false if the condition is true. In this example, output occurs if the value of the Boolean variable frozen is false: if (!frozen) { cout << "Not frozen"; } and or Temperature > 0? Both conditions must be true False Temperature ≤ 0? True Temperature < 100? False True Temperature ≥ 100? False True At least one condition must be true False True Water is liquid figure 9 cfe2_ch03_p75_130.indd 105 Water is not liquid Flowcharts for and and or Combinations 10/28/10 7:51 PM 106 Chapter 3 Decisions table 6 Boolean operators expression Value Comment 0 < 200 && 200 < 100 false Only the first condition is true. Note that the < operator has a higher precedence than the && operator. 0 < 200 || 200 < 100 true The first condition is true. 0 < 200 || 100 < 200 true The || is not a test for “either-or”. If both conditions are true, the result is true. 0 < 200 < 100 true error: The expression 0 < 200 is true, which is converted to 1. The expression 1 < 100 is true. You never want to write such an expression; see Common Error 3.5 on page 107. -10 && 10 > 0 true error: –10 is not zero. It is converted to true. You never want to write such an expression; see Common Error 3.5 on page 107. (0 < x && x < 100) || x == -1 The && operator has a higher precedence than the || operator. !(0 < 200) false 0 < 200 is true, therefore its negation is false. frozen == true frozen There is no need to compare a Boolean variable with true. frozen == false !frozen It is clearer to use ! than to compare with false. 0 < x && x < 100 || x == -1 Table 6 illustrates additional examples of evaluating Boolean operators. Self CheCk 31. 32. 33. 34. 35. practice it cfe2_ch03_p75_130.indd 106 Suppose x and y are two integers. How do you test whether both of them are zero? How do you test whether at least one of them is zero? How do you test whether exactly one of them is zero? What is the value of !!frozen? What is the advantage of using the type bool rather than strings "false"/"true" or integers 0/1? Now you can try these exercises at the end of the chapter: R3.27, P3.22, P3.24. 10/28/10 7:51 PM 3.7 Common error 3.5 Boolean Variables and operators 107 Combining multiple relational operators Consider the expression if (0 <= temp <= 100) // Error This looks just like the mathematical test 0 ≤ temp ≤ 100. Unfortunately, it is not. Let us dissect the expression 0 <= temp <= 100. The first half, 0 <= temp, is a test with outcome true or false, depending on the value of temp. The outcome of that test (true or false) is then compared against 100. Can one compare truth values and floating-point numbers? Is true larger than 100 or not? Unfortunately, to stay compatible with the C language, C++ converts false to 0 and true to 1. Therefore, the expression will always evaluate to true. You must be careful not to mix logical and arithmetic expressions in your programs. Instead, use and to combine two separate tests: if (0 <= temp && temp <= 100) ... Another common error, along the same lines, is to write if (x && y > 0) ... // Error instead of if (x > 0 && y > 0) ... Unfortunately, the compiler will not issue an error message. Instead, it converts x to true or false. Zero is converted to false, and any nonzero value is converted to true. If x is not zero, then it tests whether y is greater than 0, and finally it computes the and of these two truth values. Naturally, that computation makes no sense. Common error 3.6 Confusing && and || Conditions It is a surprisingly common error to confuse and and or conditions. A value lies between 0 and 100 if it is at least 0 and at most 100. It lies outside that range if it is less than 0 or greater than 100. There is no golden rule; you just have to think carefully. Often the and or or is clearly stated, and then it isn’t too hard to implement it. But sometimes the wording isn’t as explicit. It is quite common that the individual conditions are nicely set apart in a bulleted list, but with little indication of how they should be combined. Consider these instructions for filing a tax return. You can claim single filing status if any one of the following is true: • You were never married. • You were legally separated or divorced on the last day of the tax year. • You were widowed, and did not remarry. Since the test passes if any one of the conditions is true, you must combine the conditions with or. Elsewhere, the same instructions state that you may use the more advantageous status of married filing jointly if all five of the following conditions are true: • Your spouse died less than two years ago and you did not remarry. • You have a child whom you can claim as dependent. • That child lived in your home for all of the tax year. • You paid over half the cost of keeping up your home for this child. • You filed a joint return with your spouse the year he or she died. Because all of the conditions must be true for the test to pass, you must combine them with an and. cfe2_ch03_p75_130.indd 107 10/28/10 7:51 PM 108 Chapter 3 Decisions special topic 3.4 Short-Circuit evaluation of boolean operators When the && and || operators are computed, evaluation stops as soon as the truth value is determined. When an && is evaluated and the first condition is false, the second condition is not evaluated, because it does not matter what the outcome of the second test is. For example, consider the expression quantity > 0 && price / quantity < 10 the && and || operators are computed using short­circuit evaluation: as soon as the truth value is determined, no further conditions are evaluated. Suppose the value of quantity is zero. Then the test quantity > 0 fails, and the second test is not attempted. That is just as well, because it is illegal to divide by zero. Similarly, when the first condition of an || expression is true, then the remainder is not evaluated since the result must be true. This process is called short-circuit evaluation. In a short circuit, electricity travels along the path of least resistance. Similarly, short­circuit evaluation takes the fastest path for computing the result of a Boolean expression. special topic 3.5 de morgan’s law Humans generally have a hard time comprehending logical conditions with not operators applied to and/or expressions. De Morgan’s Law, named after the logician Augustus De Morgan (1806–1871), can be used to simplify these Boolean expressions. Suppose we want to charge a higher shipping rate if we don’t ship within the continental United States. if (!(country == "USA" && state != "AK" && state != "HI")) shipping_charge = 20.00; This test is a little bit complicated, and you have to think carefully through the logic. When it is not true that the country is USA and the state is not Alaska and the state is not Hawaii, then charge $20.00. Huh? It is not true that some people won’t be confused by this code. The computer doesn’t care, but it takes human programmers to write and maintain the code. Therefore, it is useful to know how to simplify such a condition. De Morgan’s Law has two forms: one for the negation of an and expression and one for the negation of an or expression: !(A && B) !(A || B) is the same as is the same as !A || !B !A && !B De Morgan’s law tells you how to negate && and || conditions. Pay particular attention to the fact that the and and or operators are reversed by moving the not inward. For example, the negation of “the state is Alaska or it is Hawaii”, !(state == "AK" || state == "HI") is “the state is not Alaska and it is not Hawaii”: !(state == "AK") && !(state == "HI") cfe2_ch03_p75_130.indd 108 10/28/10 7:52 PM 3.8 application: input Validation 109 That is, of course, the same as state != "AK" && state != "HI" Now apply the law to our shipping charge computation: !(country == "USA" && state != "AK" && state != "HI") is equivalent to !(country == "USA") || !(state != "AK") || !(state != "HI") That yields the simpler test country != "USA" || state == "AK" || state == "HI" To simplify conditions with negations of and or or expressions, it is usually a good idea to apply De Morgan’s Law to move the negations to the innermost level. 3.8 application: input Validation An important application for the if statement is input validation. Whenever your program accepts user input, you need to make sure that the user-supplied values are valid before you use them in your computations. Consider our elevator program. Assume that the elevator panel has buttons labeled 1 through 20 (but not 13). The following are illegal inputs: • The number 13 • Zero or a negative number • A number larger than 20 Like a quality control worker, you want to make sure that user input is correct • An input that is not a sequence of digits, such as five before processing it. In each of these cases, we will want to give an error message and exit the program. It is simple to guard against an input of 13: if (floor == 13) { cout << "Error: There is no thirteenth floor." << endl; return 1; } The statement return 1; immediately exits the main function and therefore terminates the program. It is a convention to return with the value 0 if the program completed normally, and with a non-zero value when an error was encountered. cfe2_ch03_p75_130.indd 109 10/28/10 7:52 PM 110 Chapter 3 Decisions When reading a value, check that it is within the required range. Here is how you ensure that the user doesn’t enter a number outside the valid range: if (floor <= 0 || floor > 20) { cout << "Error: The floor must be between 1 and 20." << endl; return 1; } However, dealing with an input that is not a valid integer is a more serious problem. When the statement cin >> floor; is executed, and the user types in an input that is not an integer (such as five), then the integer variable floor is not set. Instead, the input stream cin is set to a failed state. You call the fail member function to test for that failed state. if (cin.fail()) { cout << "Error: Not an integer." << endl; return 1; } Use the fail function to test whether the input stream has failed. The order of the if statements is important. You must first test for cin.fail(). After all, if the input failed, no value has been assigned to floor, and it makes no sense to compare it against other values. Input failure is quite serious in C++. Once input has failed, all subsequent attempts at input will fail as well. You will learn in Chapter 8 how to write programs that are more tolerant of bad input. For now, our goal is simply to detect bad input and to exit the program when it occurs. Here is the complete elevator program with input validation. ch03/elevator2.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 cfe2_ch03_p75_130.indd 110 #include <iostream> using namespace std; int main() { int floor; cout << "Floor: "; cin >> floor; // The following statements check various input errors if (cin.fail()) { cout << "Error: Not an integer." << endl; return 1; } if (floor == 13) { cout << "Error: There is no thirteenth floor." << endl; return 1; } if (floor <= 0 || floor > 20) { cout << "Error: The floor must be between 1 and 20." << endl; return 1; 10/28/10 7:52 PM 3.8 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 application: input Validation 111 } // Now we know that the input is valid int actual_floor; if (floor > 13) { actual_floor = floor - 1; } else { actual_floor = floor; } cout << "The elevator will travel to the actual floor " << actual_floor << endl; return 0; } program run Floor: 13 Error: There is no thirteenth floor. Self CheCk 36. 37. Consider the elevator2.cpp program. What output do you get when the input is a. 100? b. –1? c. 20? d. thirteen? Your task is to rewrite the elevator2.cpp program so that there is a single if statement with a complex condition: if (...) { cout << "Error: Bad input" << endl; return 1; } 38. What is the condition? In the Sherlock Holmes story “The Adventure of the Sussex Vampire”, the inimitable detective uttered these words: “Matilda Briggs was not the name of a young woman, Watson, … It was a ship which is associated with the giant rat of Sumatra, a story for which the world is not yet prepared.” Over a hundred years later, researchers found giant rats in Western New Guinea, another part of Indonesia. Suppose you are charged with writing a program that processes rat weights. It contains the statements double weight; cout << "Enter weight in kg: "; cin >> weight; What input checks should you supply? When processing inputs, you want to reject values that are too large. But how large is too large? These giant rats, found in Western New Guinea, are about five times the size of a city rat. cfe2_ch03_p75_130.indd 111 10/28/10 7:52 PM 112 Chapter 3 Decisions 39. Consider the following test program: int main() { int m = 1; cout << "Enter an integer: "; cin >> m; int n = 2; cout << "Enter another integer: "; cin >> n; cout << m << " " << n << endl; return 0; } Run this program and enter three at the first prompt. What happens? Why? practice it Now you can try these exercises at the end of the chapter: R3.1, R3.30, P3.26. Random Fact 3.2 When one uses a sophisticated computer program such as a tax preparation package, one is bound to attribute some intelligence to the computer. the computer asks sensible questions and makes computations that we find a mental challenge. after all, if doing one’s taxes were easy, we wouldn’t need a computer to do it for us. as programmers, however, we know that all this apparent intelligence is an illusion. human programmers have carefully “coached” the software in all possible scenarios, and it simply replays the actions and decisions that were programmed into it. Would it be possible to write computer programs that are genuinely intelligent in some sense? From the earliest days of computing, there was a sense that the human brain might be nothing but an immense computer, and that it might well be feasible to program computers to imitate some processes of human thought. seri- cfe2_ch03_p75_130.indd 112 artificial intelligence ous research into artificial intelligence began in the mid-1950s, and the first twenty years brought some impressive successes. programs that play chess—surely an activity that appears to require remarkable intellectual powers—have become so good that they now routinely beat all but the best human players. as far back as 1975, an expert­system program called Mycin gained fame for being better in diagnosing meningitis in patients than the average physician. however, there were serious setbacks as well. From 1982 to 1992, the Japanese government embarked on a massive research project, funded at over 40 billion Japanese yen. it was known as the Fifth­Generation Project. its goal was to develop new hardware and software to greatly improve the performance of expert system software. at its outset, the project created fear in other countries that the Japanese computer industry was about to become the undisputed leader in the field. however, the end results were disappointing and did little to bring artificial intelligence applications to market. From the very outset, one of the stated goals of the ai community was to produce software that could translate text from one language to another, for example from english to russian. that undertaking proved to be enormously complicated. human language appears to be much more subtle and interwoven with the human experience than had originally been thought. even the grammar-checking tools that come with word-processing programs today are more of a gimmick than a useful tool, and analyzing grammar is just the first step in translating sentences. the CYC (from encyclopedia) project, started by Douglas lenat in 1984, tries to codify the implicit assumptions that underlie human speech and writing. the team members started out analyzing news articles and asked themselves what unmentioned facts 10/28/10 7:52 PM Chapter summary 113 Chapter sUMMarY use the if statement to implement a decision. • The if statement allows a program to carry out different actions depending on the nature of the data to be processed. implement comparisons of numbers and objects. • Relational operators (< <= > >= == !=) are used to compare numbers and strings. • Lexicographic order is used to compare strings. implement complex decisions that require multiple if statements. • Multiple alternatives are required for decisions that have more than two cases. • When using multiple if statements, pay attention to the order of the conditions. are necessary to actually understand the sentences. For example, consider the sentence “last fall she enrolled in Michigan state”. the reader automatically realizes that “fall” is not related to falling down in this context, but refers to the season. While there is a state of Michigan, here Michigan state denotes the university. a priori, a computer program has none of this knowledge. the goal of the CYC project is to extract and store the requisite facts— that is, (1) people enroll in universities; (2) Michigan is a state; (3) many states have universities named x state University, often abbreviated as x state; (4) most people enroll in a university in the fall. By 1995, the project had codified about 100,000 common-sense concepts and about a million facts of knowledge relating them. even this massive amount of data has not proven sufficient for useful applications. in recent years, artificial intelligence technology has seen substantial advances. one of the most astound- cfe2_ch03_p75_130.indd 113 ing examples is the outcome of a series of “grand challenges” for autonomous vehicles posed by the Defense advanced research projects agency (Darpa). Competitors were invited to submit a computer-controlled vehicle that had to complete an obstacle course without a human driver or remote control. the first event, in 2004, was a disappointment, with none of the entrants finishing the route. in 2005, five Winner of the 2007 DARPA Urban Challenge vehicles completed a grueling 212 km course in the Mojave desert. stanford’s stanley came “in the last Grand Challenge, it didn’t in first, with an average speed of 30 really matter whether an obstacle was km/h. in 2007, Darpa moved the com- a rock or a bush, because either way petition to an “urban” environment, an you’d just drive around it. the current abandoned air force base. Vehicles had challenge is to move from just sensing to be able to interact with each other, the environment to understanding the following California traffic laws. as environment.” stanford’s sebastian thrun explained: 10/28/10 7:52 PM 114 Chapter 3 Decisions implement decisions whose branches require further decisions. • When a decision statement is contained inside the branch of another decision statement, the statements are nested. • Nested decisions are required for problems that have two levels of decision making. draw flowcharts for visualizing the control flow of a program. • Flow charts are made up of elements for tasks, input/ outputs, and decisions. • Each branch of a decision can contain tasks and further decisions. • Never point an arrow inside another branch. Condition True False design test cases for your programs. • Each branch of your program should be covered by a test case. • It is a good idea to design test cases before implementing a program. use the boolean data type to store and combine conditions that can be true or false. • • • • The Boolean type bool has two values, false and true. C++ has two Boolean operators that combine conditions: && (and) and || (or). To invert a condition, use the ! (not) operator. The && and || operators are computed using short-circuit evaluation: As soon as the truth value is determined, no further conditions are evaluated. • De Morgan’s law tells you how to negate && and || conditions. apply if statements to detect whether user input is valid. • When reading a value, check that it is within the required range. • Use the fail function to test whether the input stream has failed. reVieW exerCises r3.1 Find the errors in the following if statements. a. if x > 0 then cout << x; b. if (x > 0) ; { y = 1; } else ; { y = -1; } c. if (1 + x > pow(x, sqrt(2)) { y = y + x; } d. if (x = 1) { y++; } e. cin >> x; if (cin.fail()) { y = y + x; } r3.2 What do these code fragments print? a. int n = 1; int m = -1; if (n < -m) { cout << n; } else { cout << m; } b. int n = 1; int m = -1; if (-n >= m) { cout << n; } else { cout << m; } cfe2_ch03_p75_130.indd 114 10/28/10 7:52 PM review exercises 115 c. double x = 0; double y = 1; if (fabs(x - y) < 1) { cout << x; } else { cout << y; } d. double x = sqrt(2); double y = 2; if (x * x == y) { cout << x; } else { cout << y; } r3.3 Suppose x and y are variables of type double. Write a code fragment that sets y to x if x is positive and to 0 otherwise. r3.4 Suppose x and y are variables of type double. Write a code fragment that sets y to the absolute value of x without calling the fabs function. Use an if statement. r3.5 Explain why it is more difficult to compare floating-point numbers than integers. Write C++ code to test whether an integer n equals 10 and whether a floating-point number x equals 10. r3.6 Common Error 3.2 on page 85 explains that a C++ compiler will not report an error when you use an assignment operator instead of a test for equality, but it may issue a warning. Write a test program containing a statement if (floor = 13) What does your compiler do when you compile the program? r3.7 Each square on a chess board can be described by a letter and number, such as g5 in this example: a b c d e f 8 7 6 5 4 3 2 1 g h g5 a b c d e f 8 7 6 5 4 3 2 1 g h The following pseudocode describes an algorithm that determines whether a square with a given letter and number is dark (black) or light (white). If the letter is an a, c, e, or g If the number is odd color = "black" Else color = "white" Else If the number is even color = "black" Else color = "white" Using the procedure in Programming Tip 3.6 on page 97, trace this pseudocode with input g5. r3.8 Give a set of four test cases for the algorithm of Exercise R3.7 that covers all branches. cfe2_ch03_p75_130.indd 115 10/28/10 7:52 PM 116 Chapter 3 Decisions r3.9 In a scheduling program, we want to check whether two appointments overlap. For simplicity, appointments start at a full hour, and we use military time (with hours 0–23). The following pseudocode describes an algorithm that determines whether the appointment with start time start1 and end time end1 overlaps with the appointment with start time start2 and end time end2. If start1 > start2 s = start1 Else s = start2 If end1 < end2 e = end1 Else e = end2 If s < e The appointments overlap. Else The appointments don’t overlap. Trace this algorithm with an appointment from 10–12 and one from 11–13, then with an appointment from 10–11 and one from 12–13. r3.10 Draw a flow chart for the algorithm in Exercise R3.9. r3.11 Draw a flow chart for the algorithm in Exercise P3.12. r3.12 Draw a flow chart for the algorithm in Exercise P3.13. r3.13 Develop a set of test cases for the algorithm in Exercise R3.9. r3.14 Develop a set of test cases for the algorithm in Exercise P3.13. r3.15 Write pseudocode for a program that prompts the user for a month and day and prints out whether it is one of the following four holidays: • New Year’s Day (January 1) • Independence Day (July 4) • Veterans Day (November 11) • Christmas Day (December 25) r3.16 Write pseudocode for a program that assigns letter grades for a quiz, according to the following table: Score 90-100 80-89 70-79 60-69 < 60 Grade A B C D F r3.17 Explain how the lexicographic ordering of strings in C++ differs from the ordering of words in a dictionary or telephone book. Hint: Consider strings such as IBM, wiley.com, Century 21, and While-U-Wait. r3.18 Of the following pairs of strings, which comes first in lexicographic order? a. "Tom", "Dick" b. "Tom", "Tomato" c. "church", "Churchill" cfe2_ch03_p75_130.indd 116 10/28/10 7:52 PM review exercises 117 d. "car manufacturer", "carburetor" e. "Harry", "hairy" f. "C++", " Car" g. "Tom", "Tom" h. "Car", "Carl" i. "car", "bar" r3.19 Explain the difference between a sequence of else if clauses and nested if state- ments. Give an example for each. r3.20 Give an example of a sequence of else if clauses where the order of the tests does not matter. Give an example where the order of the tests matters. r3.21 Rewrite the condition in Section 3.3 to use < operators instead of >= operators. What is the impact on the order of the comparisons? r3.22 Give a set of test cases for the tax program in Exercise P3.18. Manually compute the expected results. r3.23 Make up another C++ code example that shows the dangling else problem, using the following statement. A student with a GPA of at least 1.5, but less than 2, is on probation. With less than 1.5, the student is failing. r3.24 Complete the following truth table by finding the truth values of the Boolean expressions for all combinations of the Boolean inputs p, q, and r. p q r false false false false false true false true false (p && q) || !r !(p && (q || !r)) ... 5 more combinations ... r3.25 True or false? A && B is the same as B && A for any Boolean conditions A and B. r3.26 The “advanced search” feature of many search engines allows you to use Boolean operators for complex queries, such as “(cats OR dogs) AND NOT pets”. Contrast these search operators with the Boolean operators in C++. r3.27 Suppose the value of b is false and the value of x is 0. What is the value of each of the following expressions? a. b && x == 0 b. b || x == 0 c. !b && x == 0 d. !b || x == 0 cfe2_ch03_p75_130.indd 117 e. b && x != 0 f. b || x != 0 g. !b && x != 0 h. !b || x != 0 10/28/10 7:52 PM 118 Chapter 3 Decisions r3.28 Simplify the following expressions. Here, b is a variable of type bool. a. b b. b c. b d. b == true == false != true != false r3.29 Simplify the following statements. Here, b is a variable of type bool and n is a variable of type int. a. if (n == 0) { b = true; } else { b = false; } (Hint: What is the value of n == 0?) b. if (n == 0) { b = false; } else { b = true; } c. b = false; if (n > 1) { if (n < 2) { b = true; } } d. if (n < 1) { b = true; } else { b = n > 2; } r3.30 What is wrong with the following program? cout << "Enter the number of quarters: "; cin >> quarters; total = total + quarters * 0.25; cout << "Total: " << total << endl; if (cin.fail()) { cout << "Input error."; } r3.31 Reading numbers is surprisingly difficult because a C++ input stream looks at the input one character at a time. First, white space is skipped. Then the stream consumes those input characters that can be a part of a number. Once the stream has recognized a number, it stops reading if it finds a character that cannot be a part of a number. However, if the first non-white space character is not a digit or a sign, or if the first character is a sign and the second one is not a digit, then the stream fails. Consider a program reading an integer: cout << "Enter the number of quarters: "; int quarters; cin >> quarters; For each of the following user inputs, circle how many characters have been read and whether the stream is in the failed state or not. a. 15.9 b. 15 9 c. +159 d. -15A9 e. Fifteen f. -Fifteen g. + 15 h. 1.5E3 i. +1+5 proGraMMinG exerCises p3.1 Write a program that reads a temperature value and the letter C for Celsius or F for Fahrenheit. Print whether water is liquid, solid, or gaseous at the given temperature at sea level. cfe2_ch03_p75_130.indd 118 10/28/10 7:52 PM programming exercises 119 p3.2 The boiling point of water drops by about one degree centigrade for every 300 meters (or 1,000 feet) of altitude. Improve the program of Exercise P3.1 to allow the user to supply the altitude in meters or feet. p3.3 Write a program that reads in three floating-point numbers and prints the largest of the three inputs. For example: Please enter three numbers: 4 9 2.5 The largest number is 9. p3.4 Write a program that reads in three strings and sorts them lexicographically. Enter three strings: Charlie Able Baker Able Baker Charlie p3.5 Write a program that reads an integer and prints how many digits the number has, by checking whether the number is ≥ 10, ≥ 100, and so on. (Assume that all integers are less than ten billion.) If the number is negative, first multiply it with –1. p3.6 Write a program that reads three numbers and prints “all the same” if they are all the same, “all different” if they are all different, and “neither” otherwise. p3.7 Write a program that reads three numbers and prints “increasing” if they are in increasing order, “decreasing” if they are in decreasing order, and “neither” otherwise. Here, “increasing” means “strictly increasing”, with each value larger than its predecessor. The sequence 3 4 4 would not be considered increasing. p3.8 Repeat Exercise P3.7, but before reading the numbers, ask the user whether increas- ing/decreasing should be “strict” or “lenient”. In lenient mode, the sequence 3 4 4 is increasing and the sequence 4 4 4 is both increasing and decreasing. p3.9 Write a program that translates a letter grade into a number grade. Letter grades are A, B, C, D, and F, possibly followed by + or –. Their numeric values are 4, 3, 2, 1, and 0. There is no F+ or F–. A + increases the numeric value by 0.3, a – decreases it by 0.3. However, an A+ has value 4.0. Enter a letter grade: BThe numeric value is 2.7. p3.10 Write a program that translates a number between 0 and 4 into the closest letter grade. For example, the number 2.8 (which might have been the average of several grades) would be converted to B–. Break ties in favor of the better grade; for example 2.85 should be a B. p3.11 Write a program that takes user input describing a playing card in the following shorthand notation: A 2 ... 10 J Q K D H S C cfe2_ch03_p75_130.indd 119 Ace Card values Jack Queen King Diamonds Hearts Spades Clubs 10/28/10 7:52 PM 120 Chapter 3 Decisions Your program should print the full description of the card. For example, Enter the card notation: QS Queen of Spades p3.12 When two points in time are compared, each given as hours (in military time, ranging from 0 and 23) and minutes, the following pseudocode determines which comes first. If hour1 < hour2 time1 comes first. Else if hour1 and hour2 are the same If minute1 < minute2 time1 comes first. Else if minute1 and minute2 are the same time1 and time2 are the same. Else time2 comes first. Else time2 comes first. Write a program that prompts the user for two points in time and prints the time that comes first, then the other time. p3.13 The following algorithm yields the season (Spring, Summer, Fall, or Winter) for a given month and day. If month is 1, 2, or 3, season = "Winter" Else if month is 4, 5, or 6, season = "Spring" Else if month is 7, 8, or 9, season = "Summer" Else if month is 10, 11, or 12, season = "Fall" If month is divisible by 3 and day >= 21 If season is "Winter", season = "Spring" Else if season is "Spring", season = "Summer" Else if season is "Summer", season = "Fall" Else season = "Winter" Write a program that prompts the user for a month and day and then prints the season, as determined by this algorithm. p3.14 Write a program that reads in two floating-point numbers and tests whether they are the same up to two decimal places. Here are two sample runs. Enter two floating-point numbers: 2.0 1.99998 They are the same up to two decimal places. Enter two floating-point numbers: 2.0 1.98999 They are different. p3.15 Write a program to simulate a bank transaction. There are two bank accounts: check- ing and savings. First, ask for the initial balances of the bank accounts; reject negative balances. Then ask for the transactions; options are deposit, withdrawal, and transfer. Then ask for the account; options are checking and savings. Then ask for the amount; reject transactions that overdraw an account. At the end, print the balances of both accounts. cfe2_ch03_p75_130.indd 120 10/28/10 7:52 PM programming exercises 121 p3.16 Write a program that reads in the name and salary of an employee. Here the salary will denote an hourly wage, such as $9.25. Then ask how many hours the employee worked in the past week. Be sure to accept fractional hours. Any overtime work (over 40 hours per week) is paid at 150 percent of the regular wage. Compute the pay. Print a paycheck for the employee. p3.17 Write a program that prompts for the day and month of the user’s birthday and then prints a horoscope. Make up fortunes for programmers, like this: Please enter your birthday (month and day): 6 16 Gemini are experts at figuring out the behavior of complicated programs. You feel where bugs are coming from and then stay one step ahead. Tonight, your style wins approval from a tough critic. Each fortune should contain the name of the astrological sign. (You will find the names and date ranges of the signs at a distressingly large number of sites on the Internet.) p3.18 Write a program that computes taxes for the following schedule: if your status is single and if the taxable income is over but not over the tax is of the amount over $0 $8,000 10% $0 $8,000 $32,000 $800 + 15% $8,000 $4,400 + 25% $32,000 $32,000 if your status is Married and if the taxable income is over but not over the tax is of the amount over $0 $16,000 10% $0 $16,000 $64,000 $1,600 + 15% $16,000 $8,800 + 25% $64,000 $64,000 p3.19 The original U.S. income tax of 1913 was quite simple. The tax was • 1 percent on the first $50,000. • 2 percent on the amount over $50,000 up to $75,000. • 3 percent on the amount over $75,000 up to $100,000. • 4 percent on the amount over $100,000 up to $250,000. • 5 percent on the amount over $250,000 up to $500,000. • 6 percent on the amount over $500,000. There was no separate schedule for single or married taxpayers. Write a program that computes the income tax according to this schedule. p3.20 The tax.cpp program uses a simplified version of the 2008 U.S. income tax schedule. Look up the tax brackets and rates for the current year, for both single and married filers, and implement a program that computes the actual income tax. cfe2_ch03_p75_130.indd 121 10/28/10 7:52 PM 122 Chapter 3 Decisions p3.21 Unit conversion. Write a unit conversion program that asks the users from which unit they want to convert (fl. oz, gal, oz, lb, in, ft, mi) and to which unit they want to convert (ml, l, g, kg, mm, cm, m, km). Reject incompatible conversions (such as gal → km). Ask for the value to be converted, then display the result: Convert from? gal Convert to? ml Value? 2.5 2.5 gal = 9462.5 ml p3.22 Write a program that prompts the user to provide a single character from the alpha- bet. Print Vowel or Consonant, depending on the user input. If the user input is not a letter (between a and z or A and Z), or is a string of length > 1, print an error message. p3.23 Roman numbers. Write a program that converts a positive integer into the Roman number system. The Roman number system has digits I V X L C D M 1 5 10 50 100 500 1,000 Numbers are formed according to the following rules. (1) Only numbers up to 3,999 are represented. (2) As in the decimal system, the thousands, hundreds, tens, and ones are expressed separately. (3) The numbers 1 to 9 are expressed as I II III IV V VI VII VIII IX 1 2 3 4 5 6 7 8 9 As you can see, an I preceding a V or X is subtracted from the value, and you can never have more than three I’s in a row. (4) Tens and hundreds are done the same way, except that the letters X, L, C and C, D, M are used instead of I, V, X, respectively. Your program should take an input, such as 1978, and convert it to Roman numerals, MCMLXXVIII. cfe2_ch03_p75_130.indd 122 10/28/10 7:52 PM programming exercises 123 p3.24 Write a program that asks the user to enter a month (1 for January, 2 for February, and so on) and then prints the number of days in the month. For February, print “28 or 29 days”. Enter a month: 5 30 days Do not use a separate if/else branch for each month. Use Boolean operators. p3.25 A year with 366 days is called a leap year. A year is a leap year if it is divisible by four (for example, 1980), except that it is not a leap year if it is divisible by 100 (for example, 1900); however, it is a leap year if it is divisible by 400 (for example, 2000). There were no exceptions before the introduction of the Gregorian calendar on October 15, 1582 (1500 was a leap year). Write a program that asks the user for a year and computes whether that year is a leap year. p3.26 Add error handling to Exercise P3.2. If the user does not enter a number when expected, or provides an invalid unit for the altitude, print an error message and end the program. engineering p3.27 Write a program that prompts the user for a wavelength value and prints a descrip- tion of the corresponding part of the electromagnetic spectrum, as given in Table 7. table 7 electromagnetic spectrum type Wavelength (m) Frequency (hz) Radio Waves > 10–1 < 3 × 109 Microwaves 10–3 to 10–1 3 × 109 to 3 × 1011 Infrared 7 × 10–7 to 10–3 3 × 1011 to 4 × 1014 Visible light 4 × 10–7 to 7 × 10–7 4 × 1014 to 7.5 × 1014 Ultraviolet 10–8 to 4 × 10–7 7.5 × 1014 to 3 × 1016 X-rays 10–11 to 10–8 3 × 1016 to 3 × 1019 Gamma rays < 10–11 > 3 × 1019 engineering p3.28 Repeat Exercise P3.27, modifying the program so that it prompts for the frequency instead. engineering p3.29 Repeat Exercise P3.27, modifying the program so that it first asks the user whether the input will be a wavelength or a frequency. engineering p3.30 A minivan has two sliding doors. Each door can be opened by either a dashboard switch, its inside handle, or its outside handle. However, the inside handles do not work if a child lock switch is activated. In order for the sliding doors to open, the gear shift must be in park, and the master unlock switch must be activated. (This book’s author is the long-suffering owner of just such a vehicle.) cfe2_ch03_p75_130.indd 123 10/28/10 7:52 PM 124 Chapter 3 Decisions Your task is to simulate a portion of the control software for the vehicle. The input is a sequence of values for the switches and the gear shift, in the following order: • Dashboard switches for left and right sliding door, child lock, and master unlock (0 for off or 1 for activated) • Inside and outside handles on the left and right sliding doors (0 or 1) • The gear shift setting (one of P N D 1 2 3 R). A typical input would be 0 0 0 1 0 1 0 0 P. Print “left door opens” and/or “right door opens” as appropriate. If neither door opens, print “both doors stay closed”. engineering p3.31 Sound level L in units of decibel (dB) is determined by L = 20 log10(p/p0) where p is the sound pressure of the sound (in Pascals, abbreviated Pa), and p0 is a reference sound pressure equal to 20 × 10–6 Pa (where L is 0 dB). The following table gives descriptions for certain sound levels. Threshold of pain Possible hearing damage Jack hammer at 1 m Traffic on a busy roadway at 10 m Normal conversation Calm library Light leaf rustling 130 dB 120 dB 100 dB 90 dB 60 dB 30 dB 0 dB Write a program that reads a value and a unit, either dB or Pa, and then prints the closest description from the list above. engineering p3.32 The electric circuit shown below is designed to measure the temperature of the gas in a chamber. 11.43 V R s = 75 Ω + Vs = 20 V + – R Voltmeter Vm – The resistor R represents a temperature sensor enclosed in the chamber. The resistance R, in Ω, is related to the temperature T, in °C, by the equation R = R0 + kT In this device, assume R0 = 100 Ω and k = 0.5. The voltmeter displays the value of the voltage, Vm , across the sensor. This voltage Vm indicates the temperature, T, of the gas according to the equation T = cfe2_ch03_p75_130.indd 124 R Vm R R R0 − = s − 0 k k k Vs − Vm k 10/28/10 7:52 PM programming exercises 125 Suppose the voltmeter voltage is constrained to the range Vmin = 12 volts ≤ Vm ≤ Vmax = 18 volts. Write a program that accepts a value of Vm and checks that it’s between 12 and 18. The program should return the gas temperature in degrees Celsius when Vm is between 12 and 18 and an error message when it isn’t. engineering p3.33 Crop damage due to frost is one of the many risks confronting farmers. The figure below shows a simple alarm circuit designed to warn of frost. The alarm circuit uses a device called a thermistor to sound a buzzer when the temperature drops below freezing. Thermistors are semiconductor devices that exhibit a temperature dependent resistance described by the equation 1 1 β −   T T0  R = R0 e where R is the resistance, in Ω, at the temperature T, in °K, and R0 is the resistance, in Ω, at the temperature T0, in°K. β is a constant that depends on the material used to make the thermistor. 9V Thermistor R 9V R3 – Buzzer + R4 R2 Comparator The circuit is designed so that the alarm will sound when R2 < R4 R + R2 R3 + R 4 The thermistor used in the alarm circuit has R0 = 33,192 Ω at T0 = 40 °C, and β = 3,310 °K. (Notice that β has units of °K. Recall that the temperature in °K is obtained by adding 273° to the temperature in °C.) The resistors R2, R3, and R4 have a resistance of 156.3 kΩ = 156,300 Ω. Write a C++ program that prompts the user for a temperature in °F and prints a message indicating whether or not the alarm will sound at that temperature. engineering p3.34 A mass m = 2 kilograms is attached to the end of a rope of length r = 3 meters. The mass is whirled around at high speed. The rope can withstand a maximum tension of T = 60 Newtons. Write a program that accepts a rotation speed v and determines if such a speed will cause the rope to break. Hint: T = m v 2 r . engineering p3.35 A mass m is attached to the end of a rope of length r = 3 meters. The rope can only be whirled around at speeds of 1, 10, 20, or 40 meters per second. The rope can withstand a maximum tension of T = 60 Newtons. Write a program where the user enters the value of the mass m, and the program determines the greatest speed at which it can be whirled without breaking the rope. Hint: T = m v 2 r . cfe2_ch03_p75_130.indd 125 10/28/10 7:52 PM 126 Chapter 3 Decisions engineering p3.36 The average person can jump off the ground with a velocity of 7 mph without fear of leaving the planet. However, if an astronaut jumps with this velocity while standing on Halley’s Comet, will the astronaut ever come back down? Create a program that allows the user to input a launch velocity (in mph) from the surface of Halley’s Comet and determine whether a jumper will return to the surface. If not, the program should calculate how much more massive the comet must be in order to return the jumper to the surface. Hint: Escape velocity is vescape = 2 GM , where G = 6.67 × 10−11 N m 2 kg 2 is R the gravitational constant, M = 1.3 × 10 22 kg is the mass of Halley’s comet, and R = 1.153 × 106 m is its radius. ansWers to selF-CheCk QUestions 1. Change the if statement to if (floor > 14) { actual_floor = floor - 2; } 2. 85. 90. 85. 3. The only difference is if original_price is 100. The statement in Self Check 2 sets discounted_price to 90; this one sets it to 80. 4. 95. 100. 95. 5. if (fuel_amount < 0.10 * fuel_capacity) { cout << "red" << endl; } else { cout << "green" << endl; } 6. (a) and (b) are both true, (c) is false. 7. floor <= 13 8. The values should be compared with ==, not =. 9. input == "Y" 10. str != "" or str.length() > 0 11. if (score_a > score_b) { cout << "A won"; } else if (score_a < score_b) { cout << "B won"; cfe2_ch03_p75_130.indd 126 10/28/10 7:52 PM answers to self-Check Questions 127 } else { cout << "Game tied"; } 12. if (x > 0) { s } else { s } else { s } = 1; if (x < 0) = -1; = 0; 13. You could first set s to one of the three values: s = 0; if (x > 0) { s = 1; } else if (x < 0) { s = -1; } 14. The if (price <= 100) can be omitted, making it clear that the else branch is the sole alternative. 15. No destruction of buildings 16. Add a branch before the final else: else if (richter < 0) { cout << "Error: Negative input" << endl; } 17. $3,200 18. No. Then the computation is 0.10 × 32000 + 0.25 (32000 – 32000). 19. No. Their individual tax is $5,200 each, and if they married, they would pay $10,400. Actually, taxpayers in higher tax brackets (which our program does not model) may pay higher taxes when they marry, a phenomenon known as the marriage penalty. 20. Change else in line 36 to else if (marital_status == "m"), and add another branch after line 47: else { cout << "Error: marital status should be s or m." << endl; } 21. The higher tax rate is only applied on the income in the higher bracket. Suppose you are single and make $31,900. Should you try to get a $200 raise? Absolutely: you get to keep 90 percent of the first $100 and 75 percent of the next $100. 22. Read temp temp < 0? True Print “Frozen” False cfe2_ch03_p75_130.indd 127 10/28/10 7:52 PM 128 Chapter 3 Decisions 23. The “True” arrow from the first decision points into the “True” branch of the sec- ond decision, creating spaghetti code. 24. Here is one solution. In Section 3.7, you will see how you can combine the conditions for a more elegant solution. Input < 0? True Status = “Error” False Input > 100? True Status = “Error” False Status = “OK” 25. Read x x < 0? True False Print cfe2_ch03_p75_130.indd 128 Print “Error” 10/28/10 7:52 PM answers to self-Check Questions 129 26. Read temp temp < 0? True Print “Ice” False temp > 100? True Print “Steam” False Print “Liquid” 27. Test Case 12 14 13 Expected Output 12 13 ? Comment Below 13th floor Above 13th floor The specification is not clear— See Section 3.8 for a version of this program with error handling 28. A boundary test case is a price of $128. A 16 percent discount should apply because the problem statement states that the larger discount applies if the price is at least $128. Thus, the expected output is $107.52. 29. Test Case 9 7.5 6.5 5 3 8.0 -1 cfe2_ch03_p75_130.indd 129 Expected Output Most structures fall Many buildings destroyed Many buildings considerably... Damage to poorly... No destruction... Most structures fall Comment Boundary case. In this program, boundary cases are not as significant since the behavior of an earthquake changes gradually. The specification is not clear—see Self Check 16 for a version of this program with error handling. 10/28/10 7:52 PM 130 Chapter 3 Decisions 30. Test Case (0.5, 0.5) (4, 2) (0, 2) (1.414, 1.414) (0, 1.9) (0, 2.1) Expected Output inside outside on the boundary on the boundary inside outside Comment Exactly on the boundary Close to the boundary Not less than 1 mm from the boundary Not less than 1 mm from the boundary 31. x == 0 && y == 0 32. x == 0 || y == 0 33. (x == 0 && y != 0) || (y == 0 && x != 0) 34. The same as the value of frozen. 35. You are guaranteed that there are no other values. With strings or integers, you would need to check that no values such as "maybe" or –1 enter your calculations. 36. (a) Error: The floor must be between 1 and 20. (b) Error: The floor must be between 1 and 20. (c) The elevator will travel to the actual floor 19 (d) Error: Not an integer. 37. cin.fail() || floor == 13 || floor <= 0 || floor > 20 38. Check for cin.fail(), to make sure a researcher didn’t supply an input such as oh my. Check for weight <= 0, since any rat must surely have a positive weight. We don’t know how giant a rat could be, but the New Guinea rats weighed no more than 2 kg. A regular house rat (rattus rattus) weighs up to 0.2 kg, so we’ll say that any weight > 10 kg was surely an input error, perhaps confusing grams and kilograms. Thus, the checks are if (cin.fail()) { cout << "Error: Not a number" << endl; return 1; } if (weight < 0) { cout << "Error: Weight cannot be negative." << endl; return 1; } if (weight > 10) { cout << "Error: Weight > 10 kg." << endl; return 1; } 39. The first input fails. The value of m is unchanged. Because a previous input failed, the next input doesn’t even try to get additional keystrokes. It also fails, and n is unchanged. The program prints 1 2. cfe2_ch03_p75_130.indd 130 10/28/10 7:52 PM Chapter 4 Loops Chapter GoaLs to implement while, for, and do loops to avoid infinite loops and off-by-one errors to understand nested loops to implement programs that read and process data sets to use a computer for simulations Chapter Contents 4.1 The while loop 132 Syntax 4.1: while statement 133 Common Error 4.1: Infinite Loops 136 Common Error 4.2: Don’t think “are We there Yet?” 137 Common Error 4.3: off-by-one errors 137 Random Fact 4.1: the First Bug 138 4.2 problem Solving: hand-Tracing 139 4.3 The for loop 142 Syntax 4.2: for statement 144 Programming Tip 4.1: Use for Loops for their Intended purpose only 147 Programming Tip 4.2: Choose Loop Bounds that Match Your task 147 Programming Tip 4.3: Count Iterations 147 4.4 The do loop 4.5 proceSSing inpuT 150 Special Topic 4.1: Clearing the Failure state 153 Special Topic 4.2: the Loop-and-a-half problem and the break statement 153 Special Topic 4.3: redirection of Input and output 154 4.6 problem Solving: SToryboardS 154 4.7 common loop algoriThmS 157 How To 4.1: Writing a Loop 162 Worked Example 4.1: Credit Card processing 4.8 neSTed loopS 165 4.9 random numberS and SimulaTionS 168 Random Fact 4.2: software piracy 172 148 Programming Tip 4.4: Flowcharts for Loops 149 131 cfe2_ch04_p131_192.indd 131 10/28/10 8:12 PM In a loop, a part of a program is repeated over and over, until a specific goal is reached. Loops are important for calculations that require repeated steps and for processing input consisting of many data items. In this chapter you will learn about loop statements in C++, as well as techniques for writing programs that process input and simulate activities in the real world. 4.1 the while Loop In this section, you will learn how to repeatedly execute statements until a goal has been reached. Recall the investment problem from Chapter 1. You put $10,000 into a bank account that earns 5 percent interest per year. How many years does it take for the account balance to be double the original investment? In Chapter 1 we developed the following algorithm for this problem: Because the interest earned also earns interest, a bank balance grows exponentially. Start with a year value of 0, a column for the interest, and a balance of $10,000. year 0 interest balance $10,000 Repeat the following steps while the balance is less than $20,000 Add 1 to the year value. Compute the interest as balance x 0.05 (i.e., 5 percent interest). Add the interest to the balance. Report the final year value as the answer. You now know how to define and update the variables in C++. What you don’t yet know is how to carry out “Repeat steps while the balance is less than $20,000”. In a particle accelerator, subatomic particles traverse a loop-shaped tunnel multiple times, gaining the speed required for physical experiments. Similarly, in computer science, statements in a loop are executed while a condition is true. 132 cfe2_ch04_p131_192.indd 132 10/28/10 8:12 PM 4.1 figure 1 the while Loop 133 Flowchart of a while Loop balance < TARGET? False True In C++, the while statement implements such a repetition (see Syntax 4.1). The code Loops execute a block of code repeatedly while a condition remains true. Increment year while (condition) { statements } keeps executing the statements while the condition is true. In our case, we want to increment the year counter and add interest while the balance is less than the target balance of $20,000: Add interest to balance while (balance < TARGET) { year++; double interest = balance * RATE / 100; balance = balance + interest; } A while statement is an example of a loop. If you draw a flowchart, the flow of execution loops again to the point where the condition is tested (see Figure 1). syntax 4.1 while statement This variable is defined outside the loop and updated in the loop. If the condition never becomes false, an infinite loop occurs. See page 136. This variable is created in each loop iteration. double balance = 0; . . Don’t put a semicolon here! . See page 80. while (balance < TARGET) { year++; These statements double interest = balance * RATE / 100; are executed while balance = balance + interest; the condition is true. } Lining up braces is a good idea. See page 79. cfe2_ch04_p131_192.indd 133 Beware of “off-by-one” errors in the loop condition. See page 137. Braces are not required if the body contains a single statement, but it’s good to always use them. See page 80. 10/28/10 8:12 PM 134 Chapter 4 Loops When you define a variable inside the loop body, the variable is created for each iteration of the loop and removed after the end of each iteration. For example, consider the interest variable in this loop: while (balance < TARGET) { year++; double interest = balance * RATE / 100; balance = balance + interest; } // interest no longer defined here A new interest variable is created in each iteration. In contrast, the balance and years variables were defined outside the loop body. That way, the same variable is used for all iterations of the loop. figure 2 execution of the doublinv Loop 1 Check the loop condition balance = 10000 0 year = 2 Execute the statements in the loop balance = 10500 1 year = 500 interest = 3 Check the loop condition again balance = 10500 1 year = year = while (balance < TARGET) { year++; double interest = balance * RATE / 100; balance = balance + interest; } The condition is still true while (balance < TARGET) { year++; double interest = balance * RATE / 100; balance = balance + interest; } . . . The condition is 4 After 15 iterations balance = The condition is true while (balance < TARGET) { year++; double interest = balance * RATE / 100; balance = balance + interest; } 20789.28 15 no longer true while (balance < TARGET) { year++; double interest = balance * RATE / 100; balance = balance + interest; } 5 Execute the statement following the loop balance = year = cfe2_ch04_p131_192.indd 134 20789.28 15 while (balance < TARGET) { year++; double interest = balance * RATE / 100; balance = balance + interest; } cout << year << endl; 10/28/10 8:12 PM 4.1 the while Loop 135 Here is the program that solves the investment problem. Figure 2 illustrates the program’s execution. ch04/doublinv.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> using namespace std; int main() { const double RATE = 5; const double INITIAL_BALANCE = 10000; const double TARGET = 2 * INITIAL_BALANCE; double balance = INITIAL_BALANCE; int year = 0; while (balance < TARGET) { year++; double interest = balance * RATE / 100; balance = balance + interest; } cout << "The investment doubled after " << year << " years." << endl; return 0; } program run The investment doubled after 15 years. Self check 1. 2. 3. 4. How many years does it take for the investment to triple? Modify the program and run it. If the interest rate is 10 percent per year, how many years does it take for the investment to double? Modify the program and run it. Modify the program so that the balance after each year is printed. How did you do that? Suppose we change the program so that the condition of the while loop is while (balance <= TARGET) 5. What is the effect on the program? Why? What does the following loop print? int n = 1; while (n < 100) { n = 2 * n; cout << n << " "; } practice it cfe2_ch04_p131_192.indd 135 Now you can try these exercises at the end of the chapter: R4.3, P4.11, P4.16. 10/28/10 8:12 PM 136 Chapter 4 Loops table 1 while Loop examples Loop Common error 4.1 output explanation i = 5; while (i > 0) { cout << i << " "; i--; } 5 4 3 2 1 When i is 0, the loop condition is false, and the loop ends. i = 5; while (i > 0) { cout << i << " "; i++; } 5 6 7 8 9 10 11 ... The i++ statement is an error causing an “infinite loop” (see Common Error 4.1 on page 136). i = 5; while (i > 5) { cout << i << " "; i--; } (No output) The statement i > 5 is false, and the loop is never executed. i = 5; while (i < 0) { cout << i << " "; i--; } (No output) The programmer probably thought, “Stop when i is less than 0”. However, the loop condition controls when the loop is executed, not when it ends (see Common Error 4.2 on page 137). i = 5; while (i > 0) ; { cout << i << " "; i--; } (No output, program does not terminate) Note the semicolon before the {. This loop has an empty body. It runs forever, checking whether i > 0 and doing nothing in the body. infinite loops A very annoying loop error is an infinite loop: a loop that runs forever and can be stopped only by killing the program or restarting the computer. If there are output statements in the program, then line after line of output flashes by on the screen. Otherwise, the program just sits there and hangs, seeming to do nothing. On some systems, you can terminate a hanging program by hitting Ctrl + C. On others, you can close the window in which the program runs. A common reason for infinite loops is forgetting to update the variable that controls the loop: year = 1; while (year <= 20) { cfe2_ch04_p131_192.indd 136 10/28/10 8:12 PM 4.1 the while Loop 137 balance = balance * (1 + RATE / 100); } Here the programmer forgot to add a year++ command in the loop. As a result, the year always stays at 1, and the loop never comes to an end. Another common reason for an infinite loop is accidentally incrementing a counter that should be decremented (or vice versa). Consider this example: year = 20; while (year > 0) { balance = balance * (1 + RATE / 100); year++; } The year variable really should have been decremented, not incremented. This is a common error because incrementing counters is so much more common than decrementing that your fingers may type the ++ on autopilot. As a consequence, year is always larger than 0, and the loop never ends. (Actually, year may eventually exceed the largest representable positive integer and wrap around to a negative number. Then the loop ends—of course, with a completely wrong result.) Common error 4.2 Like this hamster who can’t stop running in the treadmill, an infinite loop never ends. don’t Think “are we There yet?” When doing something repetitive, most of us want to know when we are done. For example, you may think, “I want to get at least $20,000,” and set the loop condition to balance >= TARGET But the while loop thinks the opposite: How long am I allowed to keep going? The correct loop condition is while (balance < TARGET) In other words: “Keep at it while the balance is less than the target.” When writing a loop condition, don’t ask, “Are we there yet?” The condition determines how long the loop will keep going. Common error 4.3 off-by-one errors Consider our computation of the number of years that are required to double an investment: int year = 0; while (balance < TARGET) { year++; double interest = balance * RATE / 100; balance = balance + interest; } cout << "The investment doubled after " << year << " years." << endl; cfe2_ch04_p131_192.indd 137 10/28/10 8:12 PM 138 Chapter 4 Loops Should year start at 0 or at 1? Should you test for balance < TARGET or for balance <= TARGET? It is easy to be off by one in these expressions. Some people try to solve off-by-one errors by randomly inserting +1 or -1 until the program seems to work—a terrible strategy. It can take a long time to compile and test all the various possibilities. Expending a small amount of mental effort is a real time saver. Fortunately, off-by-one errors are easy to avoid, simply by working through a couple of test cases and using the information from the test cases to come up an off-by-one error is with a rationale for your decisions. a common error Should year start at 0 or at 1? Look at a scenario with simple valwhen programming ues: an initial balance of $100 and an interest rate of 50 percent. After loops. think through simple test cases year 1, the balance is $150, and after year 2 it is $225, or over $200. So to avoid this type the investment doubled after 2 years. The loop executed two times, of error. incrementing year each time. Hence year must start at 0, not at 1. year 0 1 2 balance $100 $150 $225 In other words, the balance variable denotes the balance after the end of the year. At the outset, the balance variable contains the balance after year 0 and not after year 1. Next, should you use a < or <= comparison in the test? If you want to settle this question with an example, you need to find a scenario in which the final balance is exactly twice the initial balance. This happens when the interest is 100 percent. The loop executes once. Now year is 1, and balance is exactly equal to 2 * INITIAL_BALANCE. Has the investment doubled after one year? It has. Therefore, the loop should not execute again. If the test condition is balance < TARGET, the loop stops, as it should. If the test condition had been balance <= TARGET, the loop would have executed once more. In other words, you keep adding interest while the balance has not yet doubled. Random Fact 4.1 according to legend, the first bug was found in the Mark II, a huge electromechanical computer at harvard University. It really was caused by a bug—a moth was trapped in a relay switch. actually, from the note that the operator left in the log book next to the moth (see the photo), it appears as if the term “bug” had already been in active use at the time. the First Bug the pioneering computer scientist Maurice Wilkes wrote, “somehow, at the Moore school and afterwards, one had always assumed there would be no particular difficulty in getting pro- grams right. I can remember the exact instant in time at which it dawned on me that a great part of my future life would be spent finding mistakes in my own programs.” The First Bug cfe2_ch04_p131_192.indd 138 10/28/10 8:13 PM 4.2 problem solving: hand-tracing 139 4.2 problem solving: hand-tracing hand-tracing is a simulation of code execution in which you step through instructions and track the values of the variables. In Programming Tip 3.6, you learned about the method of hand-tracing. When you hand-trace code or pseudocode, you write the names of the variables on a sheet of paper, mentally execute each step of the code and update the variables. It is best to have the code written or printed on a sheet of paper. Use a marker, such as a paper clip, to mark the current line. Whenever a variable changes, cross out the old value and write the new value below. When a program produces output, also write down the output in another column. Consider this example. What value is displayed? int n = 1729; int sum = 0; while (n > 0) { int digit = n % 10; sum = sum + digit; n = n / 10; } cout << sum << endl; There are three variables: n, sum, and digit. n sum digit The first two variables are initialized with 1729 and 0 before the loop is entered. int n = 1729; int sum = 0; while (n > 0) { int digit = n % 10; sum = sum + digit; n = n / 10; } cout << sum << endl; n 1729 sum 0 digit Because n is greater than zero, enter the loop. The variable digit is set to 9 (the remainder of dividing 1729 by 10). The variable sum is set to 0 + 9 = 9. int n = 1729; int sum = 0; while (n > 0) { int digit = n % 10; sum = sum + digit; n = n / 10; } cout << sum << endl; cfe2_ch04_p131_192.indd 139 n 1729 sum 0 9 digit 9 10/28/10 8:13 PM 140 Chapter 4 Loops Finally, n becomes 172. (Recall that the remainder in the division 1729 / 10 is discarded because both arguments are integers.) Cross out the old values and write the new ones under the old ones. int n = 1729; int sum = 0; while (n > 0) { int digit = n % 10; sum = sum + digit; n = n / 10; } cout << sum << endl; n 1729 172 sum 0 9 digit n 1729 172 17 sum 0 9 11 digit n 1729 172 17 1 sum 0 9 11 18 digit n 1729 172 17 1 0 sum 0 9 11 18 19 digit 9 Now check the loop condition again. int n = 1729; int sum = 0; while (n > 0) { int digit = n % 10; sum = sum + digit; n = n / 10; } cout << sum << endl; Because n is still greater than zero, repeat the loop. Now digit becomes 2, sum is set to 9 + 2 = 11, and n is set to 17. Repeat the loop once again, setting digit to 7, sum to 11 + 7 = 18, and n to 1. Enter the loop for one last time. Now is set to 1, sum to 19, and n becomes zero. digit cfe2_ch04_p131_192.indd 140 9 2 9 2 7 9 2 7 1 10/28/10 8:13 PM 4.2 int n = 1729; int sum = 0; while (n > 0) { int digit = n % 10; sum = sum + digit; n = n / 10; } cout << sum << endl; The condition n > 0 problem solving: hand-tracing 141 Because n equals zero, this condition is not true. is now false. Continue with the statement after the loop. int n = 1729; int sum = 0; while (n > 0) { int digit = n % 10; sum = sum + digit; n = n / 10; } cout << sum << endl; n 1729 172 17 1 0 sum 0 9 11 18 19 digit output 9 2 7 1 19 This statement is an output statement. The value that is output is the value of sum, which is 19. Of course, you can get the same answer by just running the code. However, handtracing can give you an insight that you would not get if you simply ran the code. Consider again what happens in each iteration: • We extract the last digit of n. • We add that digit to sum. • We strip the digit off n. hand-tracing can help you understand how an unfamiliar algorithm works. hand-tracing can show errors in code or pseudocode. Self check In other words, the loop forms the sum of the digits in n. You now know what the loop does for any value of n, not just the one in the example. (Why would anyone want to form the sum of the digits? Operations of this kind are useful for checking the validity of credit card numbers and other forms of ID numbers—see Exercise P4.5.) Hand-tracing does not just help you understand code that works correctly. It is a powerful technique for finding errors in your code. When a program behaves in a way that you don’t expect, get out a sheet of paper and track the values of the variables as you mentally step through the code. You don’t need a working program to do hand-tracing. You can hand-trace pseudocode. In fact, it is an excellent idea to hand-trace your pseudocode before you go to the trouble of translating it into actual code, to confirm that it works correctly. 6. Hand-trace the following code, showing the value of n and the output. int n = 5; while (n >= 0) { n--; cout << n << endl; } cfe2_ch04_p131_192.indd 141 10/28/10 8:13 PM 142 Chapter 4 Loops 7. Hand-trace the following code, showing the value of n and the output. What potential error do you notice? int n = 1; while (n <= 3) { cout << n << ", "; n++; } 8. Hand-trace the following code, assuming that a is 2 and n is 4. Then explain what the code does for arbitrary values of a and n. int r = 1; int i = 1; while (i <= n) { r = r * a; i++; } 9. Trace the following code. What error do you observe? int n = 1; while (n != 50) { cout << n << endl; n = n + 10; } 10. The following pseudocode is intended to count the number of digits in the number n: count = 1 temp = n while (temp > 10) Increment count. Divide temp by 10. Trace the pseudocode for n = 123 and n = 100. What error do you find? practice it Now you can try these exercises at the end of the chapter: R4.1, R4.5. 4.3 the for Loop the for loop is used when a value runs from a starting point to an ending point with a constant increment or decrement. It often happens that you want to execute a sequence of statements a given number of times. You can use a while loop that is controlled by a counter, as in the following example: counter = 1; // Initialize the counter while (counter <= 10) // Check the counter { cout << counter << endl; counter++; // Update the counter } Because this loop type is so common, there is a special form for it, called the for loop (see Syntax 4.2). cfe2_ch04_p131_192.indd 142 10/28/10 8:13 PM 4.3 the for Loop 143 for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } Some people call this loop count-controlled. In contrast, the while loop of the preceding section can be called an event-controlled loop because it executes until an event occurs (for example, when the balance reaches the target). Another commonly-used term for a count-controlled loop is definite. You know from the outset that the loop body will be executed a definite number of times––ten times in our example. In contrast, you do not know how many iterations it takes to accumulate a target balance. Such a loop is called indefinite. The for loop neatly groups the initialization, condition, and update expressions together. However, it is important to realize that these expressions are not executed together (see Figure 3). You can visualize the for loop as an orderly sequence of steps. • The initialization is executed once, before the loop is entered. 1 • The condition is checked before each iteration. 2 5 • The update is executed after each iteration. 4 figure 3 execution of a for Loop 1 Initialize counter counter = 1 for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } 1 for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } 2 Check condition counter = 3 Execute loop body counter = 1 for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } 2 for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } 4 Update counter counter = 5 Check condition again counter = cfe2_ch04_p131_192.indd 143 2 for (counter = 1; counter <= 10; counter++) { cout << counter << endl; } 10/28/10 8:13 PM 144 Chapter 4 Loops syntax 4.2 for statement These three expressions should be related. See page 147. This initialization happens once before the loop starts. The variable i is defined only in this for loop. See page 144. The loop is executed while this condition is true. for (int i = 5; i <= 10; i++) { sum = sum + i; } This update is executed after each iteration. This loop executes 6 times. See page 147. A for loop can count down instead of up: for (counter = 10; counter >= 0; counter--) ... The increment or decrement need not be in steps of 1: for (counter = 0; counter <= 10; counter = counter + 2) ... See Table 2 on page 146 for additional variations. So far, we assumed that the counter variable had already been defined before the for loop. Alternatively, you can define a variable in the loop initialization. Such a variable is defined only in the loop: for (int counter = 1; counter <= 10; counter++) { ... } // counter no longer defined here Here is a typical use of the for loop. We want to print the balance of our savings account over a period of years, as shown in this table: Year Balance 1 10500.00 2 11025.00 3 11576.25 4 12155.06 5 12762.82 The for loop pattern applies because the variable year starts at 1 and then moves in constant increments until it reaches the target: cfe2_ch04_p131_192.indd 144 10/28/10 8:13 PM 4.3 the for Loop 145 for (int year = 1; year <= nyears; year++) { Update balance. Print year and balance. year = 1 } Here is the complete program. Figure 4 shows the corresponding flowchart. year ≤ nyears ? False True Update balance; Print year and balance year++ figure 4 Flowchart of a for Loop ch04/invtable.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 cfe2_ch04_p131_192.indd 145 #include <iostream> #include <iomanip> using namespace std; int main() { const double RATE = 5; const double INITIAL_BALANCE = 10000; double balance = INITIAL_BALANCE; int nyears; cout << "Enter number of years: "; cin >> nyears; cout << fixed << setprecision(2); for (int year = 1; year <= nyears; year++) { balance = balance * (1 + RATE / 100); cout << setw(4) << year << setw(10) << balance << endl; } return 0; } 10/28/10 8:13 PM 146 Chapter 4 Loops program run Enter 1 2 3 4 5 6 7 8 9 10 p number of years: 10 10500.00 11025.00 11576.25 12155.06 12762.82 13400.96 14071.00 14774.55 15513.28 16288.95 table 2 for Loop examples Loop Values of i for (i = 0; i <= 5; i++) 012345 Note that the loop is executed 6 times. (See Programming Tip 4.3 on page 147.) for (i = 5; i >= 0; i--) 543210 Use i-- for decreasing values. for (i = 0; i < 9; i = i + 2) 02468 Use i for (i = 0; i != 9; i = i + 2) 0 2 4 6 8 10 12 14 ... (infinite loop) You can use < or <= instead of != to avoid this problem. for (i = 1; i <= 20; i = i * 2) 1 2 4 8 16 You can specify any rule for modifying i, such as doubling it in every step. for (i = 0; i < str.length(); i++) 0 1 2 … until the last valid index of the string str In the loop body, use the expression str.substr(i, 1) to get a string containing the ith character. Self check 11. 12. p Comment = i + 2 for a step size of 2. Write the for loop of the invtable.cpp program as a while loop. How many numbers does this loop print? p for (int n = 10; n >= 0; n--) { cout << n << endl; } 13. 14. 15. practice it cfe2_ch04_p131_192.indd 146 Write a for loop that prints all even numbers between 10 and 20 (inclusive). Write a for loop that computes the sum of the integers from 1 to n. How would you modify the for loop of the invtable.cpp program to print all balances until the investment has doubled? Now you can try these exercises at the end of the chapter: R4.2, R4.7, P4.12. 10/28/10 8:13 PM 4.3 programming tip 4.1 the for Loop 147 use for loops for Their intended purpose only A for loop is an idiom for a loop of a particular form. A value runs from the start to the end, with a constant increment or decrement. The compiler won’t check whether the initialization, condition, and update expressions are related. For example, the following loop is legal: // Confusing—unrelated expressions for (cout << "Inputs: "; cin >> x; sum = sum + x) { count++; } However, programmers reading such a for loop will be confused because it does not match their expectations. Use a while loop for iterations that do not follow the for idiom. programming tip 4.2 choose loop bounds That match your Task Suppose you want to print line numbers that go from 1 to 10. Of course, you will want to use a loop for (int i = 1; i <= 10; i++) The values for i are bounded by the relation 1 ≤ i ≤ 10. Because there are ≤ on both bounds, the bounds are called symmetric. When traversing the characters in a string, it is more natural to use the bounds for (int i = 0; i < str.length(); i++) In this loop, i traverses all valid positions in the string. You can access the ith character as str. substr(i, 1). The values for i are bounded by 0 ≤ i < str.length(), with a ≤ to the left and a < to the right. That is appropriate, because str.length() is not a valid position. Such bounds are called asymmetric. In this case, it is not a good idea to use symmetric bounds: for (int i = 0; i <= str.length() - 1; i++) // Use < instead The asymmetric form is easier to understand. programming tip 4.3 count iterations Finding the correct lower and upper bounds for an iteration can be confusing. Should you start at 0 or at 1? Should you use <= or < in the termination condition? Counting the number of iterations is a very useful device for better understanding a loop. Counting is easier for loops with asymmetric bounds. The loop for (int i = a; i < b; i++) is executed b - a times. For example, the loop for (int i = 0; i < 10; i++) runs ten times, with values 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9. The loop with symmetric bounds, for (int i = a; i <= b; i++) is executed b cfe2_ch04_p131_192.indd 147 - a + 1 times. That “+1” is the source of many programming errors. 10/28/10 8:13 PM 148 Chapter 4 Loops For example, for (int i = 0; i <= 10; i++) runs 11 times. Maybe that is what you want; if not, start at 1 or use < 10. One way to visualize this “+1” error is by looking at a fence. Each section has one fence post to the left, and there is a final post on the right of the last section. Forgetting to count the last value is often called a “fence post error”. How many posts do you need for a fence with four sections? It is easy to be “off by one” with problems such as this one. 4.4 the do Loop the do loop is appropriate when the loop body must be executed at least once. p Sometimes you want to execute the body of a loop at least once and perform the loop test after the body is executed. The do loop serves that purpose: do { statements } while (condition); The body of the do loop is executed first, then the condition is tested. Some people call such a loop a post-test loop because the condition is tested after completing the loop body. In contrast, while and for loops are pre-test loops. In those loop types, the condition is tested before entering the loop body. A typical example for such a loop is input validation. Suppose you ask a user to enter a value < 100. If the user didn’t pay attention and entered a larger value, you ask again, until the value is correct. Of course, you cannot test the value until the user has entered it. This is a perfect fit for the do loop (see Figure 5): int value; do { cout << "Enter a value < 100: "; cin >> value; } while (value >= 100); cfe2_ch04_p131_192.indd 148 10/28/10 8:13 PM 4.4 figure 5 the do Loop 149 Flowchart of a do Loop Prompt user to enter a value < 100 Self check 16. 17. 18. 19. 20. practice it programming tip 4.4 Suppose that we want to check for inputs that are at least 0 and at most 100. Modify the do loop for this check. Rewrite the input check, using a while loop. What is the disadvantage of your solution? Suppose C++ didn’t have a do loop. Could you rewrite any do loop as a while loop? Write a do loop that reads integers and computes their sum. Stop when reading the value 0. Write a do loop that reads positive integers and computes their sum. Stop when reading the same value twice in a row. For example, if the input is 1 2 3 4 4, then the sum is 14 and the loop stops. Copy the input to value True value ≥ 100? False Now you can try these exercises at the end of the chapter: R4.8, R4.12, R4.13. flowcharts for loops In Section 3.5, you learned how to use flowcharts to visualize the flow of control in a program. There are two types of loops that you can include in a flowchart; they correspond to a while loop and a do loop in C++. They differ in the placement of the condition—either before or after the loop body. Condition? False Loop body True Loop body True Condition? False As described in Section 3.5, you want to avoid “spaghetti code” in your flowcharts. For loops, that means that you never want to have an arrow that points inside a loop body. cfe2_ch04_p131_192.indd 149 10/28/10 8:13 PM 150 Chapter 4 Loops 4.5 processing Input a sentinel value denotes the end of a data set, but it is not part of the data. In this section, you will learn how to read and process a sequence of input values. Whenever you read a sequence of inputs, you need to have some method of indicating the end of the sequence. Sometimes you are lucky and no input value can be zero. Then you can prompt the user to keep entering numbers, or 0 to finish the sequence. If zero is allowed but negative numbers are not, you can use –1 to indicate termination. A value that serves as a signal for termination is called a sentinel. Let’s put this technique to work in a program that computes the average of a set of salary values. In our sample program, we will use –1 as a sentinel. An employee would surely not work for a negative salary, but there may be volunteers who work for free. Inside the loop, we read an input. If the input is not –1, we process it. In order to compute the average, we need the total sum of all salaries, and the number of inputs. In the military, a sentinel guards a border or passage. In computer science, a sentinel value denotes the end of an input sequence or the border between input sequences. while (...) { cin >> salary; if (salary != -1) { sum = sum + salary; count++; } } We stay in the loop while the sentinel value is not detected. while (salary != -1) { ... } There is just one problem: When the loop is entered for the first time, no data value has been read. Be sure to initialize salary with some value other than the sentinel: double salary = 0; // Any value other than –1 will do Alternatively, use a do loop do { ... } while (salary != -1) cfe2_ch04_p131_192.indd 150 10/28/10 8:13 PM 4.5 processing Input 151 The following program reads inputs until the user enters the sentinel, and then computes and prints the average. ch04/sentinel.cpp 1 2 3 4 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 30 #include <iostream> using namespace std; int main() { double sum = 0; int count = 0; double salary = 0; cout << "Enter salaries, -1 to finish: "; while (salary != -1) { cin >> salary; if (salary != -1) { sum = sum + salary; count++; } } if (count > 0) { double average = sum / count; cout << "Average salary: " << average << endl; } else { cout << "No data" << endl; } return 0; } program run Enter salaries, -1 to finish: 10 10 40 -1 Average salary: 20 Numeric sentinels only work if there is some restriction on the input. In many cases, though, there isn’t. Suppose you want to compute the average of a data set that may contain 0 or negative values. Then you cannot use 0 or –1 to indicate the end of the input. In such a situation, you can read input data until input fails. As you have seen in Section 3.8, the condition cin.fail() is true if the preceding input has failed. For example, suppose that the input was read with these statements: double value; cin >> value; If the user enters a value that is not a number (such as Q), then the input fails. cfe2_ch04_p131_192.indd 151 10/28/10 8:13 PM 152 Chapter 4 Loops We now encounter an additional complexity. You only know that input failed after you have entered the loop and attempted to read it. To remember the failure, use a Boolean variable: You can use a Boolean variable to control a loop. set the variable to true before entering the loop, then set it to false to leave the loop. cout << "Enter values, Q to quit: "; bool more = true; while (more) { cin >> value; if (cin.fail()) { more = false; } else { Process value. } } Some programmers dislike the introduction of a Boolean variable to control a loop. Special Topic 4.2 on page 153 shows an alternative mechanism for leaving a loop. However, when reading input, there is an easier way. The expression cin >> value can be used in a condition. It evaluates to true if cin has not failed after reading value. Therefore, you can read and process a set of inputs with the following loop: cout << "Enter values, Q to quit: "; while (cin >> value) { Process value. } This loop is suitable for processing a single sequence of inputs. You will learn more about reading inputs in Chapter 8. Self check 21. 22. What does the sentinel.cpp program print when the user immediately types –1 when prompted for a value? Why does the sentinel.cpp program have two checks of the form salary != -1 23. What would happen if the definition of the salary variable in sentinel.cpp was changed to double salary = -1; 24. 25. We prompt the user “Enter values, Q to quit.” What happens when the user enters a different letter? What is wrong with the following loop for reading a sequence of values? cout << "Enter values, Q to quit: "; while (!cin.fail()) { double value; cin >> value; sum = sum + value; count++; } cfe2_ch04_p131_192.indd 152 10/28/10 8:13 PM 4.5 practice it special topic 4.1 processing Input 153 Now you can try these exercises at the end of the chapter: R4.10, P4.6, P4.7. clearing the failure State When an input operation has failed, all further input operations also fail. If you want to read two number sequences and use a letter as a sentinel, you need to clear the failure state after reading the first sentinel. Call the clear function: cout << "Enter values, Q to quit.\n"; while (cin >> values) { Process input. } cin.clear(); Suppose the user has entered 30 10 5 Q. The input of Q has caused the failure. Because only successfully processed characters are removed from the input, the Q character is still present. Read it into a dummy variable: string sentinel; cin >> sentinel; Now you can go on and read more inputs. special topic 4.2 The loop-and-a-half problem and the break Statement Some programmers dislike loops that are controlled by a Boolean variable, such as: bool more = true; while (more) { cin >> value; if (cin.fail()) { more = false; } else { Process value. } } The actual test for loop termination is in the middle of the loop, not at the top. This is called a loop and a half because one must go halfway into the loop before knowing whether one needs to terminate. As an alternative, you can use the break reserved word. while (true) { cin >> value; if (cin.fail()) { break; } Process value. } The break statement breaks out of the enclosing loop, independent of the loop condition. In the loop-and-a-half case, break statements can be beneficial. But it is difficult to lay down clear rules as to when they are safe and when they should be avoided. We do not use the break statement in this book. cfe2_ch04_p131_192.indd 153 10/28/10 8:13 PM 154 Chapter 4 Loops special topic 4.3 redirection of input and output Consider the sentinel program that computes the average value of an input sequence. If you use such a program, then it is quite likely that you already have the values in a file, and it seems a shame that you have to type them all in again. The command line interface of your operating system provides a way to link a file to the input of a program, as if all the characters in the file had actually been typed by a user. If you type sentinel < numbers.txt the program is executed. Its input instructions no longer expect Use input redirection to input from the keyboard. All input commands get their input read input from a file. from the file numbers.txt. This process is called input redirection. Use output redirection to Input redirection is an excellent tool for testing programs. capture program output in a file. When you develop a program and fix its bugs, it is boring to keep entering the same input every time you run the program. Spend a few minutes putting the inputs into a file, and use redirection. You can also redirect output. In this program, that is not terribly useful. If you run sentinel < numbers.txt > output.txt the file output.txt contains the input prompts and the output, such as Enter a value, -1 to finish: Enter a value, -1 to finish: Enter a value, -1 to finish: Enter a value, -1 to finish: Average: 15 However, redirecting output is obviously useful for programs that produce lots of output. You can print the file containing the output or edit it before you turn it in for grading. 4.6 problem solving: storyboards a storyboard consists of annotated sketches for each step in an action sequence. Developing a storyboard helps you understand the inputs and outputs that are required for a program. cfe2_ch04_p131_192.indd 154 When you design a program that interacts with a user, you need to make a plan for that interaction. What information does the user provide, and in which order? What information will your program display, and in which format? What should happen when there is an error? When does the program quit? This planning is similar to the development of a movie or a computer game, where storyboards are used to plan action sequences. A storyboard is made up of panels that show a sketch of each step. Annotations explain what is happening and note any special situations. Storyboards are also used to develop software—see Figure 6. Making a storyboard is very helpful when you begin designing a program. You need to ask yourself which information you need in order to compute the answers that the program user wants. You need to decide how to present those answers. These are important considerations that you want to settle before you design an algorithm for computing the answers. Let’s look at a simple example. We want to write a program that helps users with questions such as “How many tablespoons are in a pint?” or “How many inches are 30 centimeters?” What information does the user provide? • The quantity and unit to convert from • The unit to convert to 10/28/10 8:13 PM 4.6 problem solving: storyboards 155 figure 6 storyboard for the Design of a Web application What if there is more than one quantity? A user may have a whole table of centimeter values that should be converted into inches. What if the user enters units that our program doesn’t know how to handle, such as angstrom? What if the user asks for impossible conversions, such as inches to gallons? Let’s get started with a storyboard panel. It is a good idea to write the user inputs in a different color. (Underline them if you don’t have a color pen handy.) Converting a Sequence of Values What unit do you want to convert from? cm What unit do you want to convert to? in Allows conversion of multiple values Enter values, terminated by zero 30 30 cm = 11.81 in Format makes clear what got converted 100 100 cm = 39.37 in 0 What unit do you want to convert from? The storyboard shows how we deal with a potential confusion. A user who wants to know how many inches are 30 centimeters may not read the first prompt carefully and specify inches. But then the output is “30 in = 76.2 cm”, alerting the user to the problem. The storyboard also raises an issue. How is the user supposed to know that “cm” and “in” are valid units? Would “centimeter” and “inches” also work? What happens cfe2_ch04_p131_192.indd 155 10/28/10 8:13 PM 156 Chapter 4 Loops when the user enters a wrong unit? Let’s make another storyboard to demonstrate error handling. Handling Unknown Units (needs improvement) What unit do you want to convert from? cm What unit do you want to convert to? inches Sorry, unknown unit. What unit do you want to convert to? inch Sorry, unknown unit. What unit do you want to convert to? grrr To eliminate frustration, it is better to list the units that the user can supply. From unit (in, ft, mi, mm, cm, m, km, oz, lb, g, kg, tsp, tbsp, pint, gal): cm To unit: in No need to list the units again We switched to a shorter prompt to make room for all the unit names. Exercise R4.17 explores a different alternative. There is another issue that we haven’t addressed yet. How does the user quit the program? The first storyboard gives the impression that the program will go on forever. We can ask the user after seeing the sentinel that terminates an input sequence. Exiting the Program From unit (in, ft, mi, mm, cm, m, km, oz, lb, g, kg, tsp, tbsp, pint, gal): cm To unit: in Enter values, terminated by zero 30 30 cm = 11.81 in 0 Sentinel triggers the prompt to exit More conversions (y, n)? n (Program exits) As you can see from this case study, a storyboard is essential for developing a working program. You need to know the flow of the user interaction in order to structure your program. Self check cfe2_ch04_p131_192.indd 156 26. Provide a storyboard panel for a program that reads a number of test scores and prints the average score. The program only needs to process one set of scores. Don’t worry about error handling. 10/28/10 8:13 PM 4.7 27. 28. 29. Common Loop algorithms 157 Google has a simple interface for converting units. You just type the question, and you get the answer. Make storyboards for an equivalent interface in a C++ program. Show the “happy day” scenario in which all goes well, and show the handling of two kinds of errors. Consider a modification of the program in Self Check 26. Drop the lowest score before computing the average. Provide a storyboard for the situation in which a user only provides one score. What is the problem with implementing the following storyboard in C++? Computing Multiple Averages Enter scores: 90 80 90 100 80 The average is 88 Enter scores: 100 70 70 100 80 The average is 88 -1 is used as a sentinel to exit the program Enter scores: -1 (Program exits) 30. practice it Produce a storyboard for a program that compares the growth of a $10,000 investment for a given number of years under two interest rates. Now you can try these exercises at the end of the chapter: R4.17, R4.18, R4.19. 4.7 Common Loop algorithms In the following sections, we discuss some of the most common algorithms that are implemented as loops. You can use them as starting points for your loop designs. 4.7.1 sum and average Value Computing the sum of a number of inputs is a very common task. Keep a running total: a variable to which you add each input value. Of course, the total should be initialized with 0. double total = 0; double input; while (cin >> input) { total = total + input; } cfe2_ch04_p131_192.indd 157 10/28/10 8:13 PM 158 Chapter 4 Loops to compute an average, keep a total and a count of all values. To compute an average, count how many values you have, and divide by the count. Be sure to check that the count is not zero. double total = 0; int count = 0; double input; while (cin >> input) { total = total + input; count++; } double average = 0; if (count > 0) { average = total / count; } 4.7.2 Counting Matches to count values that fulfill a condition, check all values and increment a counter for each match. You often want to know how many values fulfill a particular condition. For example, you may want to count how many spaces are in a string. Keep a counter, a variable that is initialized with 0 and incremented whenever there is a match. int spaces = 0; for (int i = 0; i < str.length(); i++) { string ch = str.substr(i, 1); if (ch == " ") { spaces++; } } For example, if str is the string "My Fair Lady", spaces is incremented twice (when i is 2 and 7). Note that the spaces variable is declared outside the loop. We want the loop to update a single variable. The ch variable is declared inside the loop. A separate variable is created for each iteration and removed at the end of each loop iteration. This loop can also be used for scanning inputs. The following loop reads text, a word at a time, and counts the number of words with at most three letters: int short_words = 0; string input; while (cin >> input) { if (input.length() <= 3) { short_words++; } } In a loop that counts matches, a counter is incremented whenever a match is found. cfe2_ch04_p131_192.indd 158 10/28/10 8:13 PM 4.7 Common Loop algorithms 159 4.7.3 Finding the First Match If your goal is to find a match, exit the loop when the match is found. When you count the values that fulfill a condition, you need to look at all values. However, if your task is to find a match, then you can stop as soon as the condition is fulfilled. Here is a loop that finds the first space in a string. Because we do not visit all elements in the string, a while loop is a better choice than a for loop: bool found = false; int position = 0; while (!found && position < str.length()) { string ch = str.substr(position, 1); if (ch == " ") { found = true; } else { position++; } } When searching, you look at items until a match is found. If a match was found, then found is true and position is the index of the first match. If the loop did not find a match, then found remains false after the end of the loop. In the preceding example, we searched a string for a character that matches a condition. You can apply the same process for user input. Suppose you are asking a user to enter a positive value < 100. Keep asking until the user provides a correct input: bool valid = false; double input; while (!valid) { cout << "Please enter a positive value < 100: "; cin >> input; if (0 < input && input < 100) { valid = true; } else { cout << "Invalid input." << endl; } } Note that the variable input is declared outside the while loop because you will want to use the input after the loop has finished. If it had been declared inside the loop body, you would not be able to use it outside the loop. 4.7.4 Maximum and Minimum to find the largest value, update the largest value seen so far whenever you see a larger one. To compute the largest value in a sequence, keep a variable that stores the largest element that you have encountered, and update it when you find a larger one: double largest; cin >> largest; double input; while (cin >> input) { if (input > largest) { largest = input; } } This algorithm requires that there is at least one input. cfe2_ch04_p131_192.indd 159 10/28/10 8:13 PM 160 Chapter 4 Loops To find the height of the tallest bus rider, remember the largest value so far, and update it whenever you see a taller one. To compute the smallest value, simply reverse the comparison: double smallest; cin >> smallest; double input; while (cin >> input) { if (input < smallest) { smallest = input; } } 4.7.5 Comparing adjacent Values When processing a sequence of values in a loop, you sometimes need to compare a value with the value that just preceded it. For example, suppose you want to check whether a sequence of inputs contains adjacent duplicates such as 1 7 2 9 9 4 9. Now you face a challenge. Consider the typical loop for reading a value: double input; while (cin >> input) { // Now input contains the current input ... } to compare adjacent inputs, store the preceding input in a variable. How can you compare the current input with the preceding one? At any time, input contains the current input, overwriting the previous one. The answer is to store the previous input, like this: double input; double previous; while (cin >> input) { if (input == previous) { cout << "Duplicate input" << endl; } previous = input; } cfe2_ch04_p131_192.indd 160 10/28/10 8:13 PM 4.7 Common Loop algorithms 161 When comparing adjacent values, store the previous value in a variable. One problem remains. When the loop is entered for the first time, previous has not yet been set. You can solve this problem with an initial input operation outside the loop: double input; double previous; cin >> previous; while (cin >> input) { if (input == previous) { cout << "Duplicate input" << endl; } previous = input; } Self check 31. 32. 33. 34. What total is computed when no user input is provided in the algorithm in Section 4.7.1? How do you compute the total of all positive inputs? What is the value of position when no match is found in the algorithm in Section 4.7.3? What is wrong with the following loop for finding the position of the first space in a string? bool found = false; for (int position = 0; !found && position < str.length(); position++) { string ch = str.substr(position, 1); if (ch == " ") { found = true; } } 35. 36. How do you find the last space in a string? What is wrong with the following loop for finding the smallest input value? double smallest = 0; double input; while (cin >> input) { if (input < smallest) { smallest = input; } } 37. practice it cfe2_ch04_p131_192.indd 161 What happens with the algorithm in Section 4.7.5 when no input is provided at all? Now you can try these exercises at the end of the chapter: P4.8, P4.13, P4.14. 10/28/10 8:13 PM 162 Chapter 4 Loops hoW t o 4.1 writing a loop This How To walks you through the process of implementing a loop statement. We will illustrate the steps with the following example problem: Read twelve temperature values (one for each month), and display the number of the month with the highest temperature. For example, according to http://worldclimate.com, the average maximum temperatures for Death Valley are (in order by month): 18.2 22.6 26.4 31.1 36.6 42.2 45.7 44.5 40.2 33.1 24.2 17.6 In this case, the month with the highest temperature (45.7 degrees Celsius) is July, and the program should display 7. Step 1 Decide what work must be done inside the loop. Every loop needs to do some kind of repetitive work, such as • Reading another item. • Updating a value (such as a bank balance or total). • Incrementing a counter. If you can’t figure out what needs to go inside the loop, start by writing down the steps that you would take if you solved the problem by hand. For example, with the temperature reading problem, you might write Read first value. Read second value. If second value is higher than the first, set highest temperature to that value, highest month to 2. Read next value. If value is higher than the first and second, set highest temperature to that value, highest month to 3. Read next value. If value is higher than the highest temperature seen so far, set highest temperature to that value, highest month to 4. ... Now look at these steps and reduce them to a set of uniform actions that can be placed into the loop body. The first action is easy: Read next value. The next action is trickier. In our description, we used tests “higher than the first”, “higher than the first and second”, “higher than the highest temperature seen so far”. We need to settle on one test that works for all iterations. The last formulation is the most general. Similarly, we must find a general way of setting the highest month. We need a variable that stores the current month, running from 1 to 12. Then we can formulate the second loop action: If value is higher than the highest temperature, set highest temperature to that value, highest month to current month. cfe2_ch04_p131_192.indd 162 10/28/10 8:13 PM 4.7 Common Loop algorithms 163 Altogether our loop is Loop Read next value. If value is higher than the highest temperature, set highest temperature to that value, highest month to current month. Increment current month. Step 2 Specify the loop condition. What goal do you want to reach in your loop? Typical examples are: • Has the counter reached the final value? • Have you read the last input value? • Has a value reached a given threshold? In our example, we simply want the current month to reach 12. Step 3 Determine the loop type. We distinguish between two major loop types. A definite or count-controlled loop is executed a definite number of times. In an indefinite or event-controlled loop, the number of iterations is not known in advance—the loop is executed until some event happens. A typical example of the latter is a loop that reads data until a sentinel is encountered. If you know in advance how many times a loop is repeated, use a for statement. For other loops, consider the loop condition. Do you need to complete one iteration of the loop body before you can tell when to terminate the loop? In that case, you should choose a do loop. Otherwise, use a while loop. In our example, we read 12 temperature values. Therefore, we choose a for loop. Step 4 Set up variables for entering the loop for the first time. List all variables that are used and updated in the loop, and determine how to initialize them. Commonly, counters are initialized with 0 or 1, totals with 0. In our example, the variables are current month highest value highest month We need to be careful how we set up the highest temperature value. We can’t simply set it to 0. After all, our program needs to work with temperature values from Antarctica, all of which may be negative. A good option is to set the highest temperature value to the first input value. Of course, then we need to remember to only read in another 11 values, with the current month starting at 2. We also need to initialize the highest month with 1. After all, in an Australian city, we may never find a month that is warmer than January. Step 5 Process the result after the loop has finished. In many cases, the desired result is simply a variable that was updated in the loop body. For example, in our temperature program, the result is the highest month. Sometimes, the loop computes values that contribute to the final result. For example, suppose you are asked to average the temperatures. Then the loop should compute the sum, not the average. After the loop has completed, you are ready to compute the average: divide the sum by the number of inputs. cfe2_ch04_p131_192.indd 163 10/28/10 8:13 PM 164 Chapter 4 Loops Here is our complete loop: Read first value; store as highest value. highest month = 1 for (current month = 2; current month <= 12; current month++) Read next value. If value is higher than the highest value, set highest value to that value, highest month to current month. Step 6 Trace the loop with typical examples. Hand-trace your loop code, as described in Section 4.2. Choose example values that are not too complex—executing the loop 3–5 times is enough to check for the most common errors. Pay special attention when entering the loop for the first and last time. Sometimes, you want to make a slight modification to make tracing feasible. For example, when hand-tracing the investment doubling problem, use an interest rate of 20 percent rather than 5 percent. When hand-tracing the temperature loop, use 4 data values, not 12. Let’s say the data are 22.6 36.6 44.5 24.2. Here is the walkthrough: current month current value 2 3 4 36.6 44.5 24.2 highest month 1 2 3 highest value 22.6 36.6 44.5 The trace demonstrates that highest month and highest value are properly set. Step 7 Implement the loop in C++. Here’s the loop for our example. Exercise P4.4 asks you to complete the program. double highest_value; cin >> highest_value; int highest_month = 1; for (int current_month = 2; current_month <= 12; current_month++) { double next_value; cin >> next_value; if (next_value > highest_value) { highest_value = next_value; highest_month = current_month; } } cout << highest_month << endl; WorkeD exaMpLe 4.1 credit card processing This Worked Example uses a loop to remove spaces from a credit card number. Available online at www.wiley.com/college/horstmann. cfe2_ch04_p131_192.indd 164 10/28/10 8:13 PM 4.8 nested Loops 165 4.8 nested Loops When the body of a loop contains another loop, the loops are nested. a typical use of nested loops is printing a table with rows and columns. In Section 3.4, you saw how to nest two if statements. Similarly, complex iterations sometimes require a nested loop: a loop inside another loop statement. When processing tables, nested loops occur naturally. An outer loop iterates over all rows of the table. An inner loop deals with the columns in the current row. In this section you will see how to print a table. For simplicity, we will simply print powers xn, as in the table at right. Here is the pseudocode for printing the table: x1 x2 x3 x4 Print table header. For x from 1 to 10 Print table row. Print endl. How do you print a table row? You need to print a value for each exponent. This requires a second loop: For n from 1 to 4 Print xn. 1 1 1 1 2 4 8 16 3 9 27 81 … … … … 10 100 1000 10000 This loop must be placed inside the preceding loop. We say that the inner loop is nested inside the outer loop (see Figure 7). x=1 False This loop is nested in the outer loop. x ≤ 10 ? True n=1 False n≤ 4? True Print xn Print new line n++ x++ figure 7 Flowchart of a nested Loop cfe2_ch04_p131_192.indd 165 10/28/10 8:13 PM 166 Chapter 4 Loops The hour and minute displays in a digital clock are an example of nested loops. The hours loop 12 times, and for each hour, the minutes loop 60 times. There are 10 rows in the outer loop. For each x, the program prints four columns in the inner loop. Thus, a total of 10 × 4 = 40 values are printed. Following is the complete program. Note that we also use loops to print the table header. However, those loops are not nested. ch04/powtable.cpp 1 2 3 4 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 30 31 32 33 34 35 36 37 38 39 cfe2_ch04_p131_192.indd 166 #include <iostream> #include <iomanip> #include <cmath> using namespace std; int main() { const int NMAX = 4; const double XMAX = 10; // Print table header for (int n = 1; n <= NMAX; n++) { cout << setw(10) << n; } cout << endl; for (int n = 1; n <= NMAX; n++) { cout << setw(10) << "x "; } cout << endl << endl; // Print table body for (double x = 1; x <= XMAX; x++) { // Print table row for (int n = 1; n <= NMAX; n++) { cout << setw(10) << pow(x, n); } cout << endl; } return 0; } 10/28/10 8:13 PM 4.8 nested Loops 167 program run 1 2 3 x x x 1 2 3 4 5 6 7 8 9 10 1 4 9 16 25 36 49 64 81 100 1 8 27 64 125 216 343 512 729 1000 4 x 1 16 81 256 625 1296 2401 4096 6561 10000 table 3 nested Loop examples nested Loops explanation **** **** **** Prints 3 rows of 4 asterisks each. for (i = 1; i <= 4; i++) { for (j = 1; j <= 3; j++) { cout << "*"; } cout << endl; } *** *** *** *** Prints 4 rows of 3 asterisks each. for (i = 1; i <= 4; i++) { for (j = 1; j <= i; j++) { cout << "*"; } cout << endl; } * ** *** **** Prints 4 rows of lengths 1, 2, 3, and 4. for (i = 1; i <= 3; i++) { for (j = 1; j <= 5; j++) { if (j % 2 == 0) { cout << "*"; } else { cout << "-"; } } cout << endl; } -*-*-*-*-*-*- Prints asterisks in even columns, dashes in odd columns. for (i = 1; i <= 3; i++) { for (j = 1; j <= 5; j++) { if ((i + j) % 2 == 0) { cout << "*"; } else { cout << " "; } } cout << endl; } * * * * * * * * Prints a checkerboard pattern. for (i = 1; i <= 3; i++) { for (j = 1; j <= 4; j++) cout << endl; } cfe2_ch04_p131_192.indd 167 output { cout << "*"; } 10/28/10 8:13 PM 168 Chapter 4 Loops 38. Self check 39. 40. 41. Why is there a statement cout << endl in the outer loop but not in the inner loop? How would you change the program so that all powers from x0 to x5 are displayed? If you make the change in Self Check 39, how many values are displayed? What do the following nested loops display? for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { cout << i + j; } cout << endl; } 42. Write nested loops that make the following pattern of brackets: [][][][] [][][][] [][][][] practice it Now you can try these exercises at the end of the chapter: R4.23, P4.21, P4.22. 4.9 random numbers and simulations In a simulation, you use the computer to simulate an activity. You can introduce randomness by calling the random number generator. A simulation program uses the computer to simulate an activity in the real world (or an imaginary one). Simulations are commonly used for predicting climate change, analyzing traffic, picking stocks, and many other applications in science and business. In the following sections, you will learn how to implement simulations that model phenomena with a degree of randomness. 4.9.1 Generating random numbers Many events in the real world are difficult to predict with absolute precision, yet we can sometimes know the average behavior quite well. For example, a store may know from experience that a customer arrives every five minutes. Of course, that is an average—customers don’t arrive in five minute intervals. To accurately model customer traffic, you want to take that random fluctuation into account. Now, how can you run such a simulation in the computer? The C++ library has a random number generator, which produces numbers that appear to be completely random. Calling rand() yields a random integer between 0 and RAND_MAX (which is an implementation-dependent constant, typically, but not always, the largest valid int value). Call rand() again, and you get a different number. The rand function is declared in the <cstdlib> header. The following program calls the rand function ten times. ch04/random.cpp 1 2 3 4 cfe2_ch04_p131_192.indd 168 #include <iostream> #include <cstdlib> using namespace std; 10/28/10 8:13 PM 4.9 5 6 7 8 9 10 11 12 13 14 random numbers and simulations 169 int main() { for (int i = 1; i <= 10; i++) { int r = rand(); cout << r << endl; } return 0; } program run 1804289383 846930886 1681692777 1714636915 1957747793 424238335 719885386 1649760492 596516649 118964142 Actually, the numbers are not completely random. They are drawn from sequences of numbers that don’t repeat for a long time. These sequences are actually computed from fairly simple formulas; they just behave like random numbers. For that reason, they are often called pseudorandom numbers. Try running the program again. You will get the exact same output! This confirms that the random numbers are generated by formulas. However, when running simulations, you don’t always want to get the same results. To overcome this problem, specify a seed for the random number sequence. Every time you use a new seed, the random number generator starts generating a new sequence. The seed is set with the srand function. A simple value to use as a seed is the current time: srand(time(0)); Simply make this call once in your program, before generating any random numbers. Then the random numbers will be different in every program run. Also include the <ctime> header that declares the time function. 4.9.2 simulating Die tosses In actual applications, you need to transform the output from the random number generator into different ranges. For example, to simulate the throw of a die, you need random numbers between 1 and 6. Here is the general recipe for computing random integers between two bounds a and b. As you know from Programming Tip 4.3 on page 147, there are b - a + 1 values between a and b, including the bounds themselves. First compute rand() % (b - a + 1) to obtain a random value between 0 and b - a, then add a, yielding a random value between a and b: int r = rand() % (b - a + 1) + a; cfe2_ch04_p131_192.indd 169 10/28/10 8:13 PM 170 Chapter 4 Loops Here is a program that simulates the throw of a pair of dice. ch04/dice.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include #include #include #include <iostream> <string> <cstdlib> <ctime> using namespace std; int main() { srand(time(0)); for (int i = 1; i <= { int d1 = rand() % int d2 = rand() % cout << d1 << " " } cout << endl; return 0; 10; i++) 6 + 1; 6 + 1; << d2 << endl; } program run 5 2 1 5 1 6 4 6 6 5 1 1 2 1 2 4 4 1 3 2 4.9.3 the Monte Carlo Method The Monte Carlo method is an ingenious method for finding approximate solutions to problems that cannot be precisely solved. (The method is named after the famous casino in Monte Carlo.) Here is a typical example: It is difficult to compute the number p, but you can approximate it quite well with the following simulation. Simulate shooting a dart into a square surrounding a circle of radius 1. That is easy: generate random x and y coordinates between –1 and 1. cfe2_ch04_p131_192.indd 170 10/28/10 8:13 PM 4.9 random numbers and simulations 171 If the generated point lies inside the circle, we count it as a hit. That is the case when x2 + y2 ≤ 1. Because our shots are entirely random, we expect that the ratio of hits ̸ tries is approximately equal to the ratio of the areas y of the circle and the square, that is, p ̸ 4. Therefore, our 1 estimate for p is 4 × hits ̸ tries. This method yields an estimate for p, using nothing but simple arithmetic. To run the Monte Carlo simulation, you have to work a little harder with random number generation. When x –1 1 you throw a die, it has to come up with one of six faces. When throwing a dart, however, there are many possible outcomes. You must generate a random floating-point –1 number. First, generate the following value: double r = rand() * 1.0 / RAND_MAX; // Between 0 and 1 The value r is a random floating-point value between 0 and 1. (You have to multiply by 1.0 to ensure that one of the operands of the / operator is a floating-point number. The division rand() / RAND_MAX would be an integer division—see Common Error 2.3.) To generate a random value between –1 and 1, you compute: double x = -1 + 2 * r; // Between –1 and 1 As r ranges from 0 to 1, x ranges from –1 + 2 × 0 = –1 to –1 + 2 × 1 = 1. Here is the program that carries out the simulation. ch04/montecarlo.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include #include #include #include <iostream> <cstdlib> <cmath> <ctime> using namespace std; int main() { const int TRIES = 10000; srand(time(0)); int hits = 0; for (int i = 1; i <= TRIES; i++) { double r = rand() * 1.0 / RAND_MAX; // Between 0 and 1 double x = -1 + 2 * r; // Between –1 and 1 r = rand() * 1.0 / RAND_MAX; double y = -1 + 2 * r; if (x * x + y * y <= 1) { hits++; } } double pi_estimate = 4.0 * hits / TRIES; cout << "Estimate for pi: " << pi_estimate << endl; return 0; } program run Estimate for pi: 3.1504 cfe2_ch04_p131_192.indd 171 10/28/10 8:13 PM 172 Chapter 4 Loops Self check 43. How do you simulate a coin toss with the rand function? 44. How do you simulate the picking of a random playing card? 45. Why does the dice.cpp file include the <ctime> header? 46. In many games, you throw a pair of dice to get a value between 2 and 12. What is wrong with this simulated throw of a pair of dice? int sum = rand() % 11 + 2; 47. practice it How do you generate a random floating-point number between 0 and 100? Now you can try these exercises at the end of the chapter: R4.24, P4.10, P4.25. Random Fact 4.2 as you read this, you have written a few computer programs, and you have experienced firsthand how much effort it takes to write even the humblest of programs. Writing a real software product, such as a financial application or a computer game, takes a lot of time and money. Few people, and fewer companies, are going to spend that kind of time and money if they don’t have a reasonable chance to make more money from their effort. (actually, some companies give away their software in the hope that users will upgrade to more elaborate paid versions or pay for consulting. other companies give away the software that enables users to read and use files but sell the software needed to create those files. Finally, there are individuals who donate their time, out of enthusiasm, and produce programs that you can copy freely. see random Fact 9.2 for more information.) When selling software, a company must rely on the honesty of its customers. It is an easy matter for an unscrupulous person to make copies of computer programs without paying for them. In most countries that is illegal. Most governments provide legal protection, such as copyright laws and patents, to encourage the develop- cfe2_ch04_p131_192.indd 172 software piracy ment of new products. Countries that tolerate widespread piracy have found that they have an ample cheap supply of foreign software, but no local manufacturers willing to design good software for their own citizens, such as word processors in the local script or financial programs adapted to the local tax laws. When a mass market for software first appeared, vendors were enraged by the money they lost through piracy. they tried to fight back by various schemes to ensure that only the legitimate owner could use the software, such as dongles—devices that must be attached to a printer port before the software will run. Legitimate users hated these measures. they paid for the software, but they had to suffer through inconveniences, such as having multiple dongles stick out from their computer. In the United states, market pressures forced most vendors to give up on these copy protection schemes, but they are still commonplace in other parts of the world. Because it is so easy and inexpensive to pirate software, and the chance of being found out is minimal, you have to make a moral choice for yourself. If a package that you would really like to have is too expensive for your budget, do you steal it, or do you stay honest and get by with a more affordable product? of course, piracy is not limited to software. the same issues arise for other digital products as well. You may have had the opportunity to obtain copies of songs or movies without payment. or you may have been frustrated by a copy protection device on your music player that made it difficult for you to listen to songs that you paid for. admittedly, it can be difficult to have a lot of sympathy for a musical ensemble whose publisher charges a lot of money for what seems to have been very little effort on their part, at least when compared to the effort that goes into designing and implementing a software package. nevertheless, it seems only fair that artists and authors receive some compensation for their efforts. how to pay artists, authors, and programmers fairly, without burdening honest customers, is an unsolved problem at the time of this writing, and many computer scientists are engaged in research in this area. 10/28/10 8:13 PM Chapter summary 173 Chapter sUMMarY explain the flow of execution in a loop. • Loops execute a block of code repeatedly while a condition remains true. • An off-by-one error is a common error when programming loops. Think through simple test cases to avoid this type of error. use the technique of hand-tracing to analyze the behavior of a program. • Hand-tracing is a simulation of code execution in which you step through instructions and track the values of the variables. • Hand-tracing can help you understand how an unfamiliar algorithm works. • Hand-tracing can show errors in code or pseudocode. use for loops for implementing counting loops. • The for loop is used when a value runs from a starting point to an ending point with a constant increment or decrement. choose between the while loop and the do loop. • The do loop is appropriate when the loop body must be executed at least once. implement loops that read sequences of input data. • A sentinel value denotes the end of a data set, but it is not part of the data. • You can use a Boolean variable to control a loop. Set the variable to true before entering the loop, then set it to false to leave the loop. • Use input redirection to read input from a file. Use output redirection to capture program output in a file. use the technique of storyboarding for planning user interactions. • A storyboard consists of annotated sketches for each step in an action sequence. • Developing a storyboard helps you understand the inputs and outputs that are required for a program. cfe2_ch04_p131_192.indd 173 10/28/10 8:13 PM 174 Chapter 4 Loops know the most common loop algorithms. • To compute an average, keep a total and a count of all values. • To count values that fulfill a condition, check all values and increment a counter for each match. • If your goal is to find a match, exit the loop when the match is found. • To find the largest value, update the largest value seen so far whenever you see a larger one. • To compare adjacent inputs, store the preceding input in a variable. use nested loops to implement multiple levels of iteration. • When the body of a loop contains another loop, the loops are nested. A typical use of nested loops is printing a table with rows and columns. apply loops to the implementation of simulations. • In a simulation, you use the computer to simulate an activity. You can introduce randomness by calling the random number generator. reVIeW exerCIses r4.1 Provide trace tables for these loops. a. int i = 0; int j = 10; int n = 0; while (i < j) { i++; j--; n++; } b. int i = 0; int j = 0; int n = 0; while (i < 10) { i++; n = n + i + j; j++; } c. int i = 10; int j = 0; int n = 0; while (i > 0) { i--; j++; n = n + i - j; } d. int i = 0; int j = 10; int n = 0; while (i != j) { i = i + 2; j = j - 2; n++; } r4.2 What do these loops print? a. for b. for c. for d. for e. for f. for (int i = 1; i < 10; i++) { cout << i << " "; } (int i = 1; i < 10; i += 2) { cout << i << " "; } (int i = 10; i > 1; i--) { cout << i << " "; } (int i = 0; i < 10; i++) { cout << i << " "; } (int i = 1; i < 10; i = i * 2) { cout << i << " "; } (int i = 1; i < 10; i++) { if (i % 2 == 0) { cout << i << " "; } } r4.3 What is an infinite loop? On your computer, how can you terminate a program that executes an infinite loop? r4.4 What is an “off-by-one” error? Give an example from your own programming experience. cfe2_ch04_p131_192.indd 174 10/28/10 8:13 PM review exercises 175 r4.5 Write a program trace for the pseudocode in Exercise P4.9, assuming the input values are 4 7 –2 –5 0. r4.6 Is the following code legal? for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) { cout << i << " "; } cout << endl; } What does it print? Is it good coding style? If not, how would you improve it? r4.7 How often do the following loops execute? Assume that i is not changed in the loop body. a. for b. for c. for d. for e. for f. for g. for (int i = 1; i <= 10; i++) ... (int i = 0; i < 10; i++) ... (int i = 10; i > 0; i--) ... (int i = -10; i <= 10; i++) ... (int i = 10; i >= 0; i++) ... (int i = -10; i <= 10; i = i + 2) ... (int i = -10; i <= 10; i = i + 3) ... r4.8 Write pseudocode for a program that prints a calendar such as the following: Su M T 5 6 7 12 13 14 19 20 21 26 27 28 W 1 8 15 22 29 Th 2 9 16 23 30 F 3 10 17 24 31 Sa 4 11 18 25 r4.9 Write pseudocode for a program that prints a Celsius/Fahrenheit conversion table such as the following: Celsius | Fahrenheit --------+----------0 | 32 10 | 50 20 | 68 ... ... 100 | 212 r4.10 Write pseudocode for a program that reads a sequence of student records and prints the total score for each student. Each record has the student’s first and last name, followed by a sequence of test scores and a sentinel of –1. The sequence is terminated by the word END. Here is a sample sequence: Harry Morgan 94 71 86 95 -1 Sally Lin 99 98 100 95 90 -1 END Provide a trace table for this sample input. cfe2_ch04_p131_192.indd 175 10/28/10 8:13 PM 176 Chapter 4 Loops r4.11 Rewrite the following for loop into a while loop. int s = 0; for (int i = 1; i <= 10; i++) { s = s + i; } r4.12 Rewrite the following do/while loop into a while loop. int n; cin >> n; double x = double s; do { s = 1.0 n++; x = x + } while (s > 0; / (1 + n * n); s; 0.01); r4.13 Provide trace tables of the following loops. a. int s = 1; int n = 1; while (s < 10) { s = s + n; } n++; b. int s = 1; for (int n = 1; n < 5; n++) { s = s + n; } c. int s = 1; int n = 1; do { s = s + n; n++; } while (s < 10 * n); r4.14 What do the following loops print? Work out the answer by tracing the code, not by using the computer. a. int s = 1; for (int n = 1; n <= 5; n++) { s = s + n; cout << s << " "; } b. int s = 1; for (int n = 1; s <= 10; cout << s << " ") { n = n + 2; s = s + n; } c. int s = 1; int n; for (n = 1; n <= 5; n++) { s = s + n; n++; cfe2_ch04_p131_192.indd 176 10/28/10 8:13 PM review exercises 177 } cout << s << " " << n; r4.15 What do the following program segments print? Find the answers by tracing the code, not by using the computer. a. int n = 1; for (int i = 2; i < 5; i++) { n = n + i; } cout << n; b. int i; double n = 1 / 2; for (i = 2; i <= 5; i++) { n = n + 1.0 / i; } cout << i; c. double x = 1; double y = int i = 0; do { y = y / x = x + i++; } while (x < cout << i; 1; 2; y; 1.8); d. double x = 1; double y = 1; int i = 0; while (y >= 1.5) { x = x / 2; y = x + y; i++; } cout << i; r4.16 Give an example of a for loop where symmetric bounds are more natural. Give an example of a for loop where asymmetric bounds are more natural. r4.17 Add a storyboard panel for the conversion program in Section 4.6 on page 154 that shows a scenario where a user enters incompatible units. r4.18 In Section 4.6, we decided to show users a list of all valid units in the prompt. If the program supports many more units, this approach is unworkable. Give a storyboard panel that illustrates an alternate approach: If the user enters an unknown unit, a list of all known units is shown. r4.19 Change the storyboards in Section 4.6 to support a menu that asks users whether they want to convert units, see program help, or quit the program. The menu should be displayed at the beginning of the program, when a sequence of values has been converted, and when an error is displayed. r4.20 Draw a flow chart for a program that carries out unit conversions as described in Section 4.6. r4.21 In Section 4.7.4, the code for finding the largest and smallest input initializes the largest and smallest variables with an input value. Why can’t you initialize them with zero? r4.22 What are nested loops? Give an example where a nested loop is typically used. cfe2_ch04_p131_192.indd 177 10/28/10 8:13 PM 178 Chapter 4 Loops r4.23 The nested loops for (int i = 1; i <= height; i++) { for (int j = 1; j <= width; j++) { cout << "*"; } cout << endl; } display a rectangle of a given width and height, such as **** **** **** Write a single for loop that displays the same rectangle. r4.24 Suppose you design an educational game to teach children how to read a clock. How do you generate random values for the hours and minutes? r4.25 In a travel simulation, Harry will visit one of his friends that are located in three states. He has ten friends in California, three in Nevada, and two in Utah. How do you produce a random number between 1 and 3, denoting the destination state, with a probability that is proportional to the number of friends in each state? proGraMMInG exerCIses p4.1 Write programs with loops that compute a. The sum of all even numbers between 2 and 100 (inclusive). b. The sum of all squares between 1 and 100 (inclusive). c. All powers of 2 from 20 up to 220. d. The sum of all odd numbers between a and b (inclusive), where a and b are inputs. e. The sum of all odd digits of an input. (For example, if the input is 32677, the sum would be 3 + 7 + 7 = 17.) p4.2 Write programs that read a sequence of integer inputs and print a. The smallest and largest of the inputs. b. The number of even and odd inputs. c. Cumulative totals. For example, if the input is 1 7 2 9, the program should print 1 8 10 19. d. All adjacent duplicates. For example, if the input is 1 3 3 4 5 5 6 6 2, the program should print 3 5 6. p4.3 Write programs that read a line of input as a string and print a. Only the uppercase letters in the string. b. Every second letter of the string. c. The string, with all vowels replaced by an underscore. d. The number of vowels in the string. e. The positions of all vowels in the string. cfe2_ch04_p131_192.indd 178 10/28/10 8:13 PM programming exercises 179 p4.4 Complete the program in How To 4.1 on page 162. Your program should read twelve temperature values and print the month with the highest temperature. p4.5 Credit Card Number Check. The last digit of a credit card number is the check digit, which protects against transcription errors such as an error in a single digit or switching two digits. The following method is used to verify actual credit card numbers but, for simplicity, we will describe it for numbers with 8 digits instead of 16: • Starting from the rightmost digit, form the sum of every other digit. For example, if the credit card number is 43589795, then you form the sum 5 + 7 + 8 + 3 = 23. • Double each of the digits that were not included in the preceding step. Add all digits of the resulting numbers. For example, with the number given above, doubling the digits, starting with the next-to-last one, yields 18 18 10 8. Adding all digits in these values yields 1 + 8 + 1 + 8 + 1 + 0 + 8 = 27. • Add the sums of the two preceding steps. If the last digit of the result is 0, the number is valid. In our case, 23 + 27 = 50, so the number is valid. Write a program that implements this algorithm. The user should supply an 8-digit number, and you should print out whether the number is valid or not. If it is not valid, you should print out the value of the check digit that would make the number valid. p4.6 Currency conversion. Write a program that first asks the user to type today’s ex- change rate between U.S. dollars and Japanese yen, then reads U.S. dollar values and converts each to yen. Use 0 as a sentinel. p4.7 Write a program that first asks the user to type in today’s exchange rate between U.S. dollars and Japanese yen, then reads U.S. dollar values and converts each to Japanese yen. Use 0 as the sentinel value to denote the end of dollar inputs. Then the program reads a sequence of yen amounts and converts them to dollars. The second sequence is terminated by another zero value. p4.8 Write a program that reads a set of floating-point values. Ask the user to enter the values, then print • the average of the values. • the smallest of the values. • the largest of the values. • the range, that is the difference between the smallest and largest. Of course, you may only prompt for the values once. cfe2_ch04_p131_192.indd 179 10/28/10 8:13 PM 180 Chapter 4 Loops p4.9 Translate the following pseudocode for finding the minimum value from a set of inputs into a C++ program. Set a Boolean variable "first" to true. While another value has been read successfully If first is true Set the minimum to the value. Set first to false. Else if the value is less than the minimum Set the minimum to the value. Print the minimum. p4.10 Translate the following pseudocode for randomly permuting the characters in a string into a C++ program. Read a word. Repeat word.length() times Pick a random position i in the word. Pick a random position j > i in the word. Swap the letters at positions j and i. Print the word. To swap the letters, construct substrings as follows: first i middle j last Then replace the string with first + word.substr(j, 1) + middle + word.substr(i, 1) + last p4.11 Write a program that reads a word and prints each character of the word on a sepa- rate line. For example, if the user provides the input "Harry", the program prints H a r r y p4.12 Write a program that reads a word and prints the word in reverse. For example, if the user provides the input "Harry", the program prints yrraH p4.13 Write a program that reads a word and prints the number of vowels in the word. For this exercise, assume that a e i o u y are vowels. For example, if the user provides the input "Harry", the program prints 2 vowels. p4.14 Write a program that reads a word and prints the number of syllables in the word. For this exercise, assume that syllables are determined as follows: Each sequence of vowels a e i o u y, except for the last e in a word, is a vowel. However, if that algorithm yields a count of 0, change it to 1. For example, cfe2_ch04_p131_192.indd 180 Word Syllables Harry hairy hare the 2 2 1 1 10/28/10 8:13 PM programming exercises 181 p4.15 Write a program that reads a word and prints all substrings, sorted by length. For example, if the user provides the input "rum", the program prints r u m ru um rum p4.16 Write a program that reads a number and prints all of its binary digits: Print the remainder number % 2, then replace the number with number / 2. Keep going until the number is 0. For example, if the user provides the input 13, the output should be 1 0 1 1 p4.17 Mean and standard deviation. Write a program that reads a set of floating-point data values. Choose an appropriate mechanism for prompting for the end of the data set. When all values have been read, print out the count of the values, the average, and the standard deviation. The average of a data set {x1, . . ., xn} is x = ∑ xi n , where ∑ xi = x1 + … + xn is the sum of the input values. The standard deviation is s= ∑ ( xi − x ) 2 n−1 However, this formula is not suitable for the task. By the time the program has computed x , the individual xi are long gone. Until you know how to save these values, use the numerically less stable formula s= ∑ xi2 − n1 (∑ xi ) 2 n−1 You can compute this quantity by keeping track of the count, the sum, and the sum of squares as you process the input values. p4.18 The Fibonacci numbers are defined by the sequence f1 = 1 f2 = 1 fn = fn −1 + fn − 2 Reformulate that as fold1 = 1; fold2 = 1; fnew = fold1 + fold2; Fibonacci numbers describe the growth of a rabbit population. After that, discard fold2, which is no longer needed, and set fold2 to fold1 and fold1 to fnew. Repeat fnew an appropriate number of times. Implement a program that computes the Fibonacci numbers in that way. cfe2_ch04_p131_192.indd 181 10/28/10 8:13 PM 182 Chapter 4 Loops p4.19 Factoring of integers. Write a program that asks the user for an integer and then prints out all its factors. For example, when the user enters 150, the program should print 2 3 5 5 p4.20 Prime numbers. Write a program that prompts the user for an integer and then prints out all prime numbers up to that integer. For example, when the user enters 20, the program should print 2 3 5 7 11 13 17 19 Recall that a number is a prime number if it is not divisible by any number except 1 and itself. p4.21 Write a program that prints a multiplication table, like this: 1 2 2 4 3 6 ... 10 20 3 6 9 4 8 12 5 10 15 6 12 18 7 14 21 8 16 24 9 18 27 10 20 30 30 40 50 60 70 80 90 100 p4.22 Write a program that reads an integer and displays, using asterisks, a filled and hollow square, placed next to each other. For example if the side length is 5, the program should display ***** ***** ***** ***** ***** ***** * * * * * * ***** p4.23 Write a program that reads an integer and displays, using asterisks, a filled diamond of the given side length. For example, if the side length is 4, the program should display * *** ***** ******* ***** *** * p4.24 The game of Nim. This is a well-known game with a number of variants. The following variant has an interesting winning strategy. Two players alternately take marbles from a pile. In each move, a player chooses how many marbles to take. The player must take at least one but at most half of the marbles. Then the other player takes a turn. The player who takes the last marble loses. cfe2_ch04_p131_192.indd 182 10/28/10 8:13 PM programming exercises 183 You will write a program in which the computer plays against a human opponent. Generate a random integer between 10 and 100 to denote the initial size of the pile. Generate a random integer between 0 and 1 to decide whether the computer or the human takes the first turn. Generate a random integer between 0 and 1 to decide whether the computer plays smart or stupid. In stupid mode the computer simply takes a random legal value (between 1 and n̸2) from the pile whenever it has a turn. In smart mode the computer takes off enough marbles to make the size of the pile a power of two minus 1—that is, 3, 7, 15, 31, or 63. That is always a legal move, except when the size of the pile is currently one less than a power of two. In that case, the computer makes a random legal move. You will note that the computer cannot be beaten in smart mode when it has the first move, unless the pile size happens to be 15, 31, or 63. Of course, a human player who has the first turn and knows the winning strategy can win against the computer. p4.25 The Drunkard’s Walk. A drunkard in a grid of streets randomly picks one of four directions and stumbles to the next intersection, then again randomly picks one of four directions, and so on. You might think that on average the drunkard doesn’t move very far because the choices cancel each other out, but that is actually not the case. Represent locations as integer pairs (x, y). Implement the drunkard’s walk over 100 intersections and print the beginning and ending location. p4.26 The Monty Hall Paradox. Marilyn vos Savant described the following problem (loosely based on a game show hosted by Monty Hall) in a popular magazine: “Suppose you’re on a game show, and you’re given the choice of three doors: Behind one door is a car; behind the others, goats. You pick a door, say No. 1, and the host, who knows what’s behind the doors, opens another door, say No. 3, which has a goat. He then says to you, “Do you want to pick door No. 2?” Is it to your advantage to switch your choice?” Ms. vos Savant proved that it is to your advantage, but many of her readers, including some mathematics professors, disagreed, arguing that the probability would not change because another door was opened. Your task is to simulate this game show. In each iteration, randomly pick a door number between 1 and 3 for placing the car. Randomly have the player pick a door. Randomly have the game show host pick one of the two doors having a goat. Now increment a counter for strategy 1 if the player wins by switching to the third door, and increment a counter for strategy 2 if the player wins by sticking with the original choice. Run 1,000 iterations and print both counters. p4.27 The Buffon Needle Experiment. The following experiment was devised by Comte Georges-Louis Leclerc de Buffon (1707–1788), a French naturalist. A needle of length 1 inch is dropped onto paper that is ruled with lines 2 inches apart. If the needle drops onto a line, we count it as a hit. (See Figure 8.) Buffon conjectured that the quotient tries/hits approximates p. cfe2_ch04_p131_192.indd 183 figure 8 the Buffon needle experiment 10/28/10 8:13 PM 184 Chapter 4 Loops figure 9 a hit in the Buffon needle experiment yhigh 2 ylow α 0 For the Buffon needle experiment, you must generate two random numbers: one to describe the starting position and one to describe the angle of the needle with the x-axis. Then you need to test whether the needle touches a grid line. Generate the lower point of the needle. Its x-coordinate is irrelevant, and you may assume its y-coordinate ylow to be any random number between 0 and 2. The angle α between the needle and the x-axis can be any value between 0 degrees and 180 degrees (p radians). The upper end of the needle has y-coordinate yhigh = ylow + sin α The needle is a hit if yhigh is at least 2, as shown in Figure 9. Stop after 10,000 tries and print the quotient tries/hits. (This program is not suitable for computing the value of p. You need p in the computation of the angle.) engineering p4.28 In a predator-prey simulation, you compute the populations of predators and prey, using the following equations: ( ) predn +1 = predn × (1 − C + D × preyn ) preyn +1 = preyn × 1 + A − B × predn Here, A is the rate at which prey birth exceeds natural death, B is the rate of predation, C is the rate at which predator deaths exceed births without food, and D represents predator increase in the presence of food. Write a program that prompts users for these rates, the initial population sizes, and the number of periods. Then print the populations for the given number of periods. As inputs, try A = 0.1, B = C = 0.01, and D = 0.00002 with initial prey and predator populations of 1,000 and 20. engineering p4.29 Projectile flight. Suppose a cannonball is propelled straight into the air with a starting velocity v0. Any calculus book will state that the position of the ball after t seconds is s(t ) = − 1 gt 2 + v0t, where g = 9.81 m sec 2 is the gravitational force of the earth. 2 No calculus book ever mentions why someone would want to carry out such an obviously dangerous experiment, so we will do it in the safety of the computer. In fact, we will confirm the theorem from calculus by a simulation. In our simulation, we will consider how the ball moves in very short time intervals Δt. In a short cfe2_ch04_p131_192.indd 184 10/28/10 8:13 PM programming exercises 185 time interval the velocity v is nearly constant, and we can compute the distance the ball moves as Δs = vΔt. In our program, we will simply set const double DELTA_T = 0.01; and update the position by s = s + v * DELTA_T; The velocity changes constantly—in fact, it is reduced by the gravitational force of the earth. In a short time interval, Δv = –gΔt, we must keep the velocity updated as v = v - g * DELTA_T; In the next iteration the new velocity is used to update the distance. Now run the simulation until the cannonball falls back to the earth. Get the initial velocity as an input (100 m̸sec is a good value). Update the position and velocity 100 times per second, but print out the position only every full second. Also printout the values from the exact formula s(t ) = − 1 gt 2 + v0t for comparison. 2 Note: You may wonder whether there is a benefit to this simulation when an exact formula is available. Well, the formula from the calculus book is not exact. Actually, the gravitational force diminishes the farther the cannonball is away from the surface of the earth. This complicates the algebra sufficiently that it is not possible to give an exact formula for the actual motion, but the computer simulation can simply be extended to apply a variable gravitational force. For cannonballs, the calculus-book formula is actually good enough, but computers are necessary to compute accurate trajectories for higher-flying objects such as ballistic missiles. engineering p4.30 A simple model for the hull of a ship is given by 2 2  z   2x    B 1 −    1 −    T    L   2    where B is the beam, L is the length, and T is the draft. y = cfe2_ch04_p131_192.indd 185 10/28/10 8:13 PM 186 Chapter 4 Loops (Note: There are two values of y for each x and z because the hull is symmetric from starboard to port.) The cross-sectional area at a point x is called the “section” in nautical parlance. To compute it, let z go from 0 to –T in n increments, each of size T n. For each value of z, compute the value for y. Then sum the areas of trapezoidal strips. At right are the strips where n = 4. Write a program that reads in values for B, L, T, x, and n and then prints out the cross-sectional area at x. engineering p4.31 Radioactive decay of radioactive materials can be modeled by the equation A = A0e-t (log 2̸h), where A is the amount of the material at time t, A0 is the amount at time 0, and h is the half-life. Technetium-99 is a radioisotope that is used in imaging of the brain. It has a half-life of 6 hours. Your program should display the relative amount A ̸ A0 in a patient body every hour for 24 hours after receiving a dose. engineering p4.32 The photo at left shows an electric device called a “transformer”. Transformers are often constructed by wrapping coils of wire around a ferrite core. The figure below illustrates a situation that occurs in various audio devices such as cell phones and music players. In this circuit, a transformer is used to connect a speaker to the output of an audio amplifier. R0 = 20 Ω + – 1:n Vs = 40 V Amplifier Rs = 8 Ω Speaker Transformer The symbol used to represent the transformer is intended to suggest two coils of wire. The parameter n of the transformer is called the “turns ratio” of the transformer. (The number of times that a wire is wrapped around the core to form a coil is called the number of turns in the coil. The turns ratio is literally the ratio of the number of turns in the two coils of wire.) When designing the circuit, we are concerned primarily with the value of the power delivered to the speakers—that power causes the speakers to produce the sounds we want to hear. Suppose we were to connect the speakers directly to the amplifier without using the transformer. Some fraction of the power available from the amplifier would get to the speakers. The rest of the available power would be lost in the amplifier itself. The transformer is added to the circuit to increase the fraction of the amplifier power that is delivered to the speakers. The power, Ps , delivered to the speakers is calculated using the formula   nVs Ps = Rs    n 2 R + R  0 s cfe2_ch04_p131_192.indd 186 2 10/28/10 8:13 PM answers to self-Check Questions 187 Write a C++ program that models the circuit shown and varies the turns ratio from 0.01 to 2 in 0.01 increments, then determines the value of the turns ratio that maximizes the power delivered to the speakers. ansWers to seLF-CheCk QUestIons 1. 23 years. 2. 7 years. 3. Add a statement cout << balance << endl; as the last statement in the while loop. 4. The program prints the same output. This is because the balance after 14 years is slightly below $20,000, and after 15 years, it is slightly above $20,000. 5. 2 4 8 16 32 64 128 Note that the value 128 is printed even though it is larger than 100. 6. 7. n 5 4 3 2 1 0 -1 n 1 2 3 4 output 4 3 2 1 0 -1 output 1, 1, 2, 1, 2, 3, There is a comma after the last value. Usually, commas are between values only. 8. a 2 n 4 r 1 2 4 8 16 i 1 2 3 4 5 The code computes an. 9. n 1 11 21 31 41 51 61 ... output 1 11 21 31 41 51 61 This is an infinite loop. n is never equal to 50. 10. count n 1 123 2 12.3 3 1.23 cfe2_ch04_p131_192.indd 187 10/28/10 8:13 PM 188 Chapter 4 Loops This yields the correct answer. The number 123 has 3 digits. count n 1 100 2 10 This yields the wrong answer. The number 100 also has 3 digits. The loop condition should have been while (temp >= 10) 11. int year = 1; while (year <= nyears) { balance = balance * (1 + RATE / 100); cout << setw(4) << year << setw(10) << balance << endl; year++; } 12. 11 numbers: 10 9 8 7 6 5 4 3 2 1 0 13. for (int i = 10; i <= 20; i = i + 2) { cout << n << endl; } 14. int sum = 0; for (int i = 1; i <= n; i++) { sum = sum + i; } 15. for (int year = 1; balance <= 2 * INITIAL_BALANCE; year++) However, it is best not to use a for loop in this case because the loop condition does not relate to the year variable. A while loop would be a better choice. 16. do { cout << "Enter a value between 0 and 100: "; cin >> value; } while (value < 0 || value > 100); 17. int value = 100; while (value >= 100) { cout << "Enter a value < 100: "; cin >> value; } Here, the variable value had to be initialized with an artificial value to ensure that the loop is entered at least once. 18. Yes. The do loop do { body } while (condition); is equivalent to this while loop: bool first = true; while (first || condition) { body; first = false; } 19. int x; int sum = 0; do { cfe2_ch04_p131_192.indd 188 10/28/10 8:13 PM answers to self-Check Questions 189 cin >> x; sum = sum + x; } while (x != 0); 20. int x = 0; int previous; do { previous = x; cin >> x; sum = sum + x; } while (previous != x); 21. No data 22. The first check ends the loop after the sentinel has been read. The second check ensures that the sentinel is not processed as an input value. 23. The while loop would never be entered. The user would never be prompted for in- put. Since count stays 0, the program would then print "No data". 24. The stream also fails. A more accurate prompt would have been: “Enter values, a key other than a digit to quit.” But that might be more confusing to the program user who would need now ponder which key to choose. 25. You don’t know whether the input fails until after you try reading input. 26. Computing the average Enter scores, Q to quit: 90 80 90 100 80 Q The average is 88 (Program exits) 27. Simple conversion Your conversion question: How many in are 30 cm Only one value can be converted. 30 cm = 11.81 in Run program again for another question (Program exits) Unknown unit Your conversion question: How many inches are 30 cm? Unknown unit: inches Known units are in, ft, mi, mm, cm, m, km, oz, lb, g, kg, tsp, tbsp, pint, gal (Program exits) Program doesn’t understand question syntax Your conversion question: What is an ångström? Please formulate your question as “How many (unit) are (value) (unit)?” (Program exits) cfe2_ch04_p131_192.indd 189 10/28/10 8:13 PM 190 Chapter 4 Loops 28. One score is not enough Enter scores, Q to quit: 90 Q Error: At least two scores are required. (Program exits) 29. It would not be possible to implement this interface using the C++ features we have covered up to this point. There is no way for the program to know when the first set of inputs ends. (When you read numbers with cin >> value, it is your choice whether to put them on a single line or multiple lines.) 30. Comparing two interest rates First interest rate in percent: 5 Second interest rate in percent: 10 Years: 5 Year 5% 10% 0 1 2 3 4 5 10000.00 10500.00 11025.00 11576.25 12155.06 12762.82 10000.00 11000.00 12100.00 13310.00 14641.00 16105.10 This row clarifies that 1 means the end of the first year 31. The total is zero. 32. double total = 0; double input; while (cin >> input) { if (input > 0) { total = total + input; } } 33. position is str.length(). 34. The loop will stop when a match is found, but you cannot access the match because position is not defined outside the loop. 35. Start the loop at the end of string: bool found = false; int position = str.length() - 1; while (!found && position >= 0) { string ch = str.substr(position, 1); if (ch == " ") { found = true; } else { position--; } } 36. Unless the input contains zero or negative numbers, the smallest value is incorrectly computed as 0. 37. When executing cin >> previous, cin fails and previous is unchanged. The statement cin >> input also fails, and the while loop is never entered. 38. All values in the inner loop should be displayed on the same line. cfe2_ch04_p131_192.indd 190 10/28/10 8:13 PM answers to self-Check Questions 191 39. Change lines 14, 19, and 31 to for (int n = 0; n <= NMAX; n++). Change NMAX to 5. 40. 60: The outer loop is executed 10 times, and the inner loop 6 times. 41. 0123 1234 2345 42. for (int i = 1; i <= 3; i++) { for (int j = 1; j <= 4; j++) { cout << "[]"; } cout << endl; } 43. Compute rand() % 2, and use 0 for heads, 1 for tails, or the other way around. 44. Compute rand() % 4 and associate the numbers 0 ... 3 with the four suits. Then com- pute rand() % 13 and associate the numbers 0 ... 12 with Jack, Ace, 2 ... 10, Queen, and King. 45. It is required for calling the time function. 46. The call will produce a value between 2 and 12, but all values have the same probability. When throwing a pair of dice, the number 7 is six times as likely as the number 2. The correct formula is int sum = rand() % 6 + rand() % 6 + 2; 47. rand() * 100.0 / RAND_MAX cfe2_ch04_p131_192.indd 191 10/28/10 8:13 PM This page intentionally left blank 5 Chapter FunCtions Chapter Goals to be able to implement functions to become familiar with the concept of parameter passing to appreciate the importance of function comments to develop strategies for decomposing complex tasks into simpler ones to be able to determine the scope of a variable to recognize when to use value and reference parameters Chapter Contents 194 5.1 Functions as Black Boxes 5.2 implementing Functions 196 Syntax 5.1: Function Definition 197 Programming Tip 5.1: Function Comments 199 5.3 parameter passing 199 Programming Tip 5.2: Do not Modify parameter Variables 201 5.4 return Values 202 Common Error 5.1: Missing return Value 203 Special Topic 5.1: Function Declarations 203 How To 5.1: implementing a Function 204 Worked Example 5.1: Matching and replacing parts of a string Worked Example 5.2: using a Debugger 5.5 Functions Without return Values 206 5.6 proBlem solVing: reusaBle Functions 208 5.7 proBlem solVing: stepWise reFinement 210 Programming Tip 5.3: Keep Functions short 216 Programming Tip 5.4: tracing Functions 216 Programming Tip 5.5: stubs 217 Worked Example 5.3: Calculating a Course Grade 5.8 VariaBle scope and gloBal VariaBles 218 Programming Tip 5.6: avoid Global Variables 220 5.9 reFerence parameters 220 Programming Tip 5.7: prefer return Values to reference parameters 225 Special Topic 5.2: Constant references 225 5.10 recursiVe Functions (optional) 226 How To 5.2: thinking recursively 229 Random Fact 5.1: the explosive Growth of personal Computers 230 193 cfe2_ch05_p193_248.indd 193 10/26/10 6:14 PM Functions are a fundamental building block of C++ programs. a function packages a computation into a form that can be easily understood and reused. (the person in the image to the left is executing the function “make two cups of espresso”.) in this chapter, you will learn how to design and implement your own functions. using the process of stepwise refinement, you will be able to break up complex tasks into sets of cooperating functions. 5.1 Functions as Black Boxes a function is a named sequence of instructions. A function is a sequence of instructions with a name. You have already encountered several functions. For example, the function named pow, which was introduced in Chapter 2, contains instructions to compute a power xy. Moreover, every C++ program has a function called main. You call a function in order to execute its instructions. For example, consider the following program: int main() { double z = pow(2, 3); ... } By using the expression pow(2, 3), main calls the pow function, asking it to compute the power 23. The main function is temporarily suspended. The instructions of the pow function execute and compute the result. The pow function returns its result (that is, the value 8) back to main, and the main function resumes execution (see Figure 1). main Pass 2 and 3 to pow pow Compute 23 Wait Pass result to caller Use result Figure 1 execution Flow During a Function Call 194 cfe2_ch05_p193_248.indd 194 10/26/10 6:14 PM 5.1 Functions as Black Boxes 195 Inputs 2, 3 pow Output 8 Figure 2 arguments are supplied when a function is called. the return value is the result that the function computes. the pow Function as a Black Box When another function calls the pow function, it provides “inputs”, such as the expressions 2 and 3 in the call pow(2, 3). These expressions are called arguments. This terminology avoids confusion with other inputs, such as those provided by a human user. Similarly, the “output” that the pow function computes is called the return value. Functions can have multiple arguments, but they have only one return value. Note that the return value of a function is returned to the calling function, not displayed on the screen. For example, suppose your program contains a statement double z = pow(2, 3); When the pow function returns its result, the return value is stored in the variable z. If you want the value to be displayed, you need to add a statement such as cout << z. At this point, you may wonder how the pow function performs its job. For example, how does pow(2, 3) compute that 23 is 8? By multiplying 2 × 2 × 2? With logarithms? Fortunately, as a user of the function, you don’t need to know how the function is implemented. You just need to know the specification of the function: If you provide arguments x and y, the function returns xy. Engineers use the term black box for a device with a given specification but unknown implementation. You can think of pow as a black box, as shown in Figure 2. When you design your own functions, you will want to make them appear as black boxes to other programmers. Those programmers want to use your functions without knowing what goes on inside. Even if you are the only person working on a program, making each function into a black box pays off: there are fewer details that you need to keep in mind. Although a thermostat is usually white, you can think of it as a black box. The input is the desired temperature, and the output is a signal to the heater or air conditioner. cfe2_ch05_p193_248.indd 195 10/26/10 6:14 PM 196 Chapter 5 Functions 1. selF check 2. 3. 4. practice it Consider the function call pow(3, 2). What are the arguments and return value? What is the return value of the function call pow(pow(2, 2), 2)? The ceil function in the C++ standard library takes a single argument x and returns the smallest integer ≥ x. What is the return value of ceil(2.3)? It is possible to determine the answer to Self Check 3 without knowing how the ceil function is implemented. Use an engineering term to describe this aspect of the ceil function. Now you can try these exercises at the end of the chapter: R5.1, P5.1. 5.2 implementing Functions In this section, you will learn how to implement a function from a given specification. We will use a very simple example: a function to compute the volume of a cube with a given side length. The cube_volume function uses a given side length to compute the volume of a cube. When defining a function, you provide a name for the function, a variable for each argument, and a type for the result. When writing this function, you need to • Pick a name for the function (cube_volume). • Declare a variable for each argument (double called parameter variables. • Specify the type of the return value (double). side_length). These variables are Put all this information together to form the first line of the function’s definition: double cube_volume(double side_length) Next, specify the body of the function: the statements that are executed when the function is called. The volume of a cube of side length s is s × s × s. However, for greater clarity, our parameter variable has been called side_length, not s, so we need to compute side_ length * side_length * side_length. We will store this value in a variable called volume: double volume = side_length * side_length * side_length; In order to return the result of the function, use the return statement: return volume; The body of a function is enclosed in braces. Here is the complete function: double cube_volume(double side_length) { double volume = side_length * side_length * side_length; return volume; } cfe2_ch05_p193_248.indd 196 10/26/10 6:14 PM 5.2 implementing Functions 197 The return statement gives the function’s result to the caller. Let’s put this function to use. We’ll supply a main function that calls the cube_volume function twice. int main() { double result1 = cube_volume(2); double result2 = cube_volume(10); cout << "A cube with side length 2 has volume " << result1 << endl; cout << "A cube with side length 10 has volume " << result2 << endl; return 0; } When the function is called with different arguments, the function returns different results. Consider the call cube_volume(2). The argument 2 corresponds to the side_ length parameter variable. Therefore, in this call, side_length is 2. The function computes side_length * side_length * side_length, or 2 * 2 * 2. When the function is called with a different argument, say 10, then the function computes 10 * 10 * 10. Now we combine both functions into a test program. Because main calls cube_ volume, the cube_volume function must be known before the main function is defined. This is easily achieved by placing cube_volume first and main last in the source file. (See Special Topic 5.1 on page 203 for an alternative.) syntax 5.1 Function Definition Type of return value Name of function Function body, executed when function is called. Type of parameter variable Name of parameter variable double cube_volume(double side_length) { double volume = side_length * side_length * side_length; return volume; } return statement exits function and returns result. See page 202. cfe2_ch05_p193_248.indd 197 10/26/10 6:14 PM 198 Chapter 5 Functions Here is the complete program. Note the comment that describes the behavior of the function. (Programming Tip 5.1 on page 199 describes the format of the comment.) p ch05/cube.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> using namespace std; /** Computes the volume of a cube. @param side_length the side length of the cube @return the volume */ double cube_volume(double side_length) { double volume = side_length * side_length * side_length; return volume; } int main() { double result1 = cube_volume(2); double result2 = cube_volume(10); cout << "A cube with side length 2 has volume " << result1 << endl; cout << "A cube with side length 10 has volume " << result2 << endl; return 0; } program run A cube with side length 2 has volume 8 A cube with side length 10 has volume 1000 selF check 5. 6. 7. 8. 9. What is the value of cube_volume(3)? What is the value of cube_volume(cube_volume(2))? Provide an alternate implementation of the body of the cube_volume function by calling the pow function. Define a function square_area that computes the area of a square of a given side length. Consider this function: int mystery(int x, int y) { double result = (x + y) / (y - x); return result; } What is the result of the call mystery(2, practice it cfe2_ch05_p193_248.indd 198 3)? Now you can try these exercises at the end of the chapter: R5.2, P5.2, P5.7. 10/26/10 6:14 PM 5.3 programming tip 5.1 parameter passing 199 Function comments Whenever you write a function, you should comment its behavior. Comments are for human readers, not compilers, and there is no universal standard for the layout of a function comment. In this book, we will use the following layout: /** Computes the volume of a cube. @param side_length the side length of the cube @return the volume */ double cube_volume(double side_length) { double volume = side_length * side_length * side_length; return volume; } Function comments explain the purpose of the function, the meaning of the parameter variables and return value, as well as any special requirements. This particular documentation style is borrowed from the Java programming language. It is widely supported by C++ tools as well, for example by the Doxygen tool (www.doxygen.org). The first line of the comment describes the purpose of the function. Each @param clause describes a parameter variable and the @return clause describes the return value. Note that the function comment does not document the implementation (how the function does what it does) but rather the design (what the function does, its inputs, and its results). The comment allows other programmers to use the function as a “black box”. 5.3 parameter passing parameter variables hold the argument values supplied in the function call. In this section, we examine the mechanism of passing arguments into functions. When a function is called, its parameter variables are created (Another commonly used term for a parameter variable is formal parameter.) In the function call, an expression is supplied for each parameter variable, called the argument. (Another commonly used term for this expression is actual parameter.) Each parameter variable is initialized with the value of the corresponding argument. Consider the function call illustrated in Figure 3: double result1 = cube_volume(2); pie(fruit) pie(fruit) A recipe for a fruit pie may say to use any kind of fruit. Here, “fruit” is an example of a parameter variable. Apples and cherries are examples of arguments. cfe2_ch05_p193_248.indd 199 10/26/10 6:14 PM 200 Chapter 5 Functions 1 Function call result1 = double result1 = cube_volume(2); side_length = 2 Initializing function parameter variable result1 = double result1 = cube_volume(2); side_length = 3 About to return to the caller double volume = side_length * side_length * side_length; return volume; 4 After function call 2 result1 = side_length = 2 volume = 8 result1 = 8 double result1 = cube_volume(2); Figure 3 parameter passing • The parameter variable side_length of the cube_volume function is created. 1 • The parameter variable is initialized with the value of the argument that was passed in the call. In our case, side_length is set to 2. 2 • The function computes the expression side_length * side_length * side_length, which has the value 8. That value is stored in the variable volume. 3 • The function returns. All of its variables are removed. The return value is transferred to the caller, that is, the function calling the cube_volume function. 4 Now consider what happens in a subsequent call cube_volume(10). A new parameter variable is created. (Recall that the previous parameter variable was removed when the first call to cube_volume returned.) It is initialized with the argument 10, and the process repeats. After the second function call is complete, its variables are again removed. Like any other variables, parameter variables can only be set to values of compatible types. For example, the side_length parameter variable of the cube_volume function has type double. It is valid to call cube_volume(2.0) or cube_volume(2). In the latter call, the integer 2 is automatically converted to the double value 2.0. However, a call cube_ volume("two") is not legal. selF check 10. p What does this program print? Use a diagram like Figure 3 to find the answer. double mystery(int x, int y) { double z = x + y; z = z / 2.0; cfe2_ch05_p193_248.indd 200 10/26/10 6:14 PM 5.3 parameter passing 201 return z; } int main() { int a = 4; int b = 7; cout << mystery(a, b) << endl; } 11. What does this program print? Use a diagram like Figure 3 to find the answer. int mystery(int x) { int y = x * x; return y; } int main() { int a = 4; cout << mystery(a + 1) << endl; } 12. What does the following program print? Use a diagram like Figure 3 to find the answer. int mystery(int n) { n++; n++; return n; } int main() { int a = 5; cout << mystery(a) << endl; } practice it programming tip 5.2 Now you can try these exercises at the end of the chapter: R5.5, P5.10. do not modify parameter Variables In C++, a parameter variable is just like any other variable. You can modify the values of the parameter variables in the body of a function. For example, int total_cents(int dollars, int cents) { cents = dollars * 100 + cents; // Modifies return cents; } parameter variable However, many programmers find this practice confusing. It mixes the concept of a parameter (input to the function) with that of a variable (storage for a value). To avoid the confusion, simply introduce a separate variable: int total_cents(int dollars, int cents) { int result = dollars * 100 + cents; return result; } cfe2_ch05_p193_248.indd 201 10/26/10 6:14 PM 202 Chapter 5 Functions 5.4 return Values the return statement terminates a function call and yields the function result. You use the return statement to specify the result of a function. When the return statement is processed, the function exits immediately. This behavior is convenient for handling exceptional cases at the beginning: double cube_volume(double side_length) { if (side_length < 0) { return 0; } double volume = side_length * side_length * side_length; return volume; } If the function is called with a negative value for side_length, then the function returns 0 and the remainder of the function is not executed. (See Figure 4.) In the preceding example, each return statement returned a constant or a variable. Actually, the return statement can return the value of any expression. Instead of saving the return value in a variable and returning the variable, it is often possible to eliminate the variable and return a more complex expression: double cube_volume(double side_length) { return side_length * side_length * side_length; } It is important that every branch of a function return a value. Consider the following incorrect function: double cube_volume(double side_length) { if (side_length >= 0) { return side_length * side_length * side_length; } // Error } Suppose you call cube_volume with a negative value for the side length. Of course, you aren’t supposed to call that, but it might happen as the result of a coding error. Because the if condition is not true, the return statement is not executed. However, the function must return something. Depending on the circumstances, the compiler might side_length < 0? True return 0 False volume = side_length × side_length × side_length return volume Figure 4 cfe2_ch05_p193_248.indd 202 A return statement exits a Function immediately 10/26/10 6:14 PM 5.4 return Values 203 flag this as an error, or the function might return a random value. Protect against this problem by returning some safe value: double cube_volume(double side_length) { if (side_length >= 0) { return side_length * side_length * side_length; } return 0; } The last statement of every function ought to be a return statement. This ensures that some value gets returned when the function reaches the end. Common error 5.1 missing return Value A function always needs to return something. If the code of the function contains several branches, make sure that each one of them returns a value: int sign(double x) { if (x < 0) { return -1; } if (x > 0) { return 1; } // Error: missing return value } if x equals 0 This function computes the sign of a number: –1 for negative numbers and +1 for positive numbers. If the argument is zero, however, no value is returned. Most compilers will issue a warning in this situation, but if you ignore the warning and the function is ever called with an argument of 0, a random quantity will be returned. special topic 5.1 Function declarations It is a compile-time error to call a function that the compiler does not know, just as it is an error to use an undefined variable. You can avoid this error if you define all functions before they are first used. First define lower-level helper functions, then the mid-level workhorse functions, and finally main in your program. Some programmers prefer to list the main function first in their programs. If you share that preference, you need to learn how to declare the other functions at the top of the program. A declaration of a function lists the return type, function name, and parameter variables, but it contains no body: double cube_volume(double side_length); This is an advertisement that promises that the function is implemented elsewhere. It is easy to distinguish declarations from definitions: Declarations end in a semicolon, whereas definitions are followed by a {...} block. Declarations are also called prototypes. In a function prototype, the names of the parameters are optional. You could also write double cube_volume(double); However, it is a good idea to include parameter names in order to document the purpose of each parameter. The declarations of common functions such as pow are contained in header files. If you have a look inside <cmath>, you will find the declaration of pow and the other math functions. cfe2_ch05_p193_248.indd 203 10/26/10 6:14 PM 204 Chapter 5 Functions Here is an alternate organization of the cube.cpp file: #include <iostream> using namespace std; // Declaration of cube_volume double cube_volume(double side_length); int main() { double result1 = cube_volume(2); // Use of cube_volume double result2 = cube_volume(10); cout << "A cube with side length 2 has volume " << result1 << endl; cout << "A cube with side length 10 has volume " << result2 << endl; return 0; } // Definition of cube_volume double cube_volume(double side_length) { return side_length * side_length * side_length; } If you prefer this approach, go ahead and use it in your programs. You just need to be aware of one drawback. Whenever you change the name of a function or one of the parameter types, you need to fix it in both places: in the declaration and in the definition. hoW t o 5.1 implementing a Function A function is a computation that can be used multiple times with different parameters, either in the same program or in different programs. Whenever a computation is needed more than once, turn it into a function. To illustrate this process, suppose that you are helping archaeologists who research Egyptian pyramids. You have taken on the task of writing a function that determines the volume of a pyramid, given its height and base length. step 1 Describe what the function should do. Provide a simple English description, such as “Compute the volume of a pyramid whose base is a square.” step 2 Determine the function’s “inputs”. Make a list of all the parameters that can vary. It is common for beginners to implement functions that are overly specific. For example, you may know that the great pyramid of Giza, the largest of the Egyptian pyramids, has a height of 146 meters and a base length of 230 meters. You should not use these numbers in your calculation, even if the original problem only asked about the great pyramid. It is just as easy—and far more useful—to write a function that computes the volume of any pyramid. cfe2_ch05_p193_248.indd 204 10/26/10 6:14 PM 5.4 return Values 205 In our case, the parameters are the pyramid’s height and base length. At this point, we have enough information to document the function: /** Computes the volume of a pyramid whose base is a square. @param height the height of the pyramid @param base_length the length of one side of the pyramid’s base @return the volume of the pyramid */ step 3 Determine the types of the parameter variables and the return value. The height and base length can both be floating-point numbers. Therefore, we will choose the type double for both parameter variables. The computed volume is also a floating-point number, yielding a return type of double. Therefore, the function will be defined as double pyramid_volume(double height, double base_length) step 4 Write pseudocode for obtaining the desired result. In most cases, a function needs to carry out several steps to find the desired answer. You may need to use mathematical formulas, branches, or loops. Express your function in pseudocode. An Internet search yields the fact that the volume of a pyramid is computed as volume = 1/3 x height x base area Since the base is a square, we have base area = base length x base length Using these two equations, we can compute the volume from the parameter variables. step 5 Implement the function body. In our example, the function body is quite simple. Note the use of the return the result. return statement to { double base_area = base_length * base_length; return height * base_area / 3; } step 6 Test your function. After implementing a function, you should test it in isolation. Such a test is called a unit test. Work out test cases by hand, and make sure that the function produces the correct results. For example, for a pyramid with height 9 and base length 10, we expect the area to be 1̸3 × 9 × 100 = 300. If the height is 0, we expect an area of 0. int main() { cout << "Volume: " cout << "Expected: cout << "Volume: " cout << "Expected: return 0; } << pyramid_volume(9, 10) << endl; 300" << pyramid_volume(0, 10) << endl; 0" The output confirms that the function worked as expected: Volume: 300 Expected: 300 Volume: 0 Expected: 0 cfe2_ch05_p193_248.indd 205 10/26/10 6:14 PM 206 Chapter 5 Functions WorKeD exaMple 5.1 matching and replacing parts of a string This Worked Example creates a function to find the first occurrence of one string in a given string and replace it with a second string. WorKeD exaMple 5.2 using a debugger In this Worked Example, you will learn how to use a debugger to find errors in a program. 5.5 Functions Without return Values use a return type of void to indicate that a function does not return a value. Sometimes, you need to carry out a sequence of instructions that does not yield a value. If that instruction sequence occurs multiple times, you will want to package it into a function. In C++, you use the return type void to indicate the absence of a return value. Here is a typical example. Your task is to print a string in a box, like this: ------!Hello! ------- A void function returns no value, but it can produce output. However, different strings can be substituted for Hello. A function for this task can be defined as follows: void box_string(string str) Now you develop the body of the function in the usual way, by formulating a general method for solving the task. • Print a line that contains the - character n + 2 times, where n is the length of the string. • Print a line containing the string, surrounded with a ! to the left and right. • Print another line containing the - character n + 2 times. Here is the function implementation: /** Prints a string in a box. @param str the string to print */ void box_string(string str) { Available online at www.wiley.com/college/horstmann. cfe2_ch05_p193_248.indd 206 10/26/10 6:14 PM 5.5 Functions Without return Values 207 int n = str.length(); for (int i = 0; i < n + 2; i++) { cout << "-"; } cout << endl; cout << "!" << str << "!" << endl; for (int i = 0; i < n + 2; i++) { cout << "-"; } cout << endl; } Note that this function doesn’t compute any value. It performs some actions and then returns to the caller. (See the sample program ch05/box.cpp.) Because there is no return value, you cannot use box_string in an expression. You can call box_string("Hello"); but not result = box_string("Hello"); // Error: box_string doesn’t return a result. If you want to return from a void function before reaching the end, you use a return statement without a value. For example, void box_string(string str) { int n = str.length(); if (n == 0) { return; // Return immediately } ... } selF check 13. How do you generate the following printout, using the box_string function? ------!Hello! ------------!World! ------- 14. What is wrong with the following statement? cout << box_string("Hello"); 15. 16. Implement a function shout that prints a line consisting of a string followed by three exclamation marks. For example, shout("Hello") should print Hello!!!. The function should not return a value. How would you modify the box_string function to leave a space around the string that is being boxed, like this: --------! Hello ! --------- 17. practice it cfe2_ch05_p193_248.indd 207 The box_string function contains the code for printing a line of - characters twice. Place that code into a separate function print_line, and use that function to simplify box_string. What is the code of both functions? Now you can try these exercises at the end of the chapter: R5.4, P5.24. 10/26/10 6:14 PM 208 Chapter 5 Functions 5.6 problem solving: reusable Functions eliminate replicated code or pseudocode by defining a function. You have used many functions from the C++ standard library. These functions have been provided as a part of standard C++ so that programmers need not recreate them. Of course, the C++ library doesn’t cover every conceivable need. You will often be able to save yourself time by designing your own functions that can be used for multiple problems. When you write nearly identical code or pseudocode multiple times, either in the same program or in separate programs, consider introducing a function. Here is a typical example of code replication: int hours; do { cout << "Enter a value between 0 and 23: "; cin >> hours; } while (hours < 0 || hours > 23); int minutes; do { cout << "Enter a value between 0 and 59: "; cin >> minutes; } while (minutes < 0 || minutes > 59); This program segment reads two variables, making sure that each of them is within a certain range. It is easy to extract the common behavior into a function: /** Prompts a user to enter a value up to a given maximum until the user provides a valid input. @param high the largest allowable input @return the value provided by the user (between 0 and high, inclusive) */ int read_int_up_to(int high) { int input; do { cout << "Enter a value between 0 and " << high << ": "; cin >> input; } while (input < 0 || input > high); return input; } Then use this function twice: int hours = read_int_up_to(23); int minutes = read_int_up_to(59); We have now removed the replication of the loop—it only occurs once, inside the function. Note that the function can be reused in other programs that need to read integer values. However, we should consider the possibility that the smallest value need not always be zero. cfe2_ch05_p193_248.indd 208 10/26/10 6:14 PM 5.6 problem solving: reusable Functions 209 When carrying out the same task multiple times, use a function. Design your functions to be reusable. supply parameter variables for the values that can vary when the function is reused. Here is a better alternative: /** Prompts a user to enter a value within a given range until the user provides a valid input. @param low the smallest allowable input @param high the largest allowable input @return the value provided by the user (between low and high, inclusive) */ int read_int_between(int low, int high) { int input; do { cout << "Enter a value between " << low << " and " << high << ": "; cin >> input; } while (input < low || input > high); return input; } In our program, we call int hours = read_int_between(0, 23); Another program can call int month = read_int_between(1, 12); In general, you will want to provide parameter variables for the values that vary when a function is reused. selF check 18. Consider the following statements: int total_pennies = static_cast<int>(100 * total + 0.5); int total_tax_pennies = static_cast<int>(100 * total * tax_rate + 0.5); 19. Introduce a function to reduce code duplication. Consider this code that prints a page number on the left or right side of a page: if (page % 2 == 0) { cout << page << endl; } else { cout << setw(80) << page << endl; } Introduce a function with return type bool to make the condition in the if statement easier to understand. cfe2_ch05_p193_248.indd 209 10/26/10 6:14 PM 210 Chapter 5 Functions 20. Consider the following function that computes compound interest for an account with an initial balance of $10,000 and an interest rate of 5 percent: double balance(int years) { return 10000 * pow(1.05, years); } 21. How can you make this function more reusable? The comment explains what the following loop does. Use a function instead. // Counts the number of spaces int spaces = 0; for (int i = 0; i < input.length(); i++) { if (input.substr(i, 1) == " ") { spaces++; } } 22. practice it In Self Check 21, you were asked to implement a function that counts spaces. How can you generalize it so that it can count any character? Why would you want to do this? Now you can try these exercises at the end of the chapter: R5.7, P5.23. 5.7 problem solving: stepwise refinement use the process of stepwise refinement to decompose complex tasks into simpler ones. cfe2_ch05_p193_248.indd 210 One of the most powerful strategies for problem solving is the process of stepwise refinement. To solve a difficult task, break it down into simpler tasks. Then keep breaking down the simpler tasks into even simpler ones, until you are left with tasks that you know how to solve. Here is an application of this process to a problem of everyday life. You get up in the morning and simply must get coffee. How do you get coffee? You see whether you can get someone else, such as your mother or A production process is broken down mate, to bring you some. If that fails, you into sequences of assembly steps. must make coffee. How do you make coffee? If there is instant coffee available, you can make instant coffee. How do you make instant coffee? Simply boil water and mix the boiling water with the instant coffee. How do you boil water? If there is a microwave, then you fill a cup with water, place it in the microwave and heat it for three minutes. Otherwise, you fill a kettle with water and heat it on the stove until the water comes to a boil. On the other hand, if you don’t have instant coffee, you must brew coffee. How do you brew coffee? You add water to the coffee maker, put in a filter, grind coffee, put the coffee in the filter, and turn the coffee maker on. How do you grind coffee? You add coffee beans to the coffee grinder and push the button for 60 seconds. Figure 5 shows a flowchart view of the coffee-making solution. Refinements are shown as expanding boxes. In C++, you implement a refinement as a function. For example, a function brew_coffee would call grind_coffee, and it would be called from a function make_coffee. Let us apply the process of stepwise refinement to a programming problem. 10/26/10 6:14 PM 5.7 Yes Get coffee Make coffee Do you No have a microwave? Fill cup with water Put cup in microwave 211 Can you No ask someone ? Ask for coffee Yes problem solving: stepwise refinement Fill kettle with water Yes Do you have instant coffee? No Make instant coffee Brew coffee Boil water Add water to coffee maker Mix water and instant coffee Add filter to coffee maker Bring to a boil Heat 3 min. Grind coffee beans Add coffee beans to grinder Grind 60 sec. Add coffee beans to filter Turn coffee maker on Figure 5 Flowchart of Coffee-Making solution When printing a check, it is customary to write the check amount both as a number (“$274.15”) and as a text string (“two hundred seventy four dollars and 15 cents”). Doing so reduces the recipient’s temptation to add a few digits in front of the amount. For a human, this isn’t particularly difficult, but how can a computer do this? There is no built-in function that turns 274 into "two hundred seventy four". We need to program this function. Here is the description of the function we want to write: /** Turns a number into its English name. @param number a positive integer < 1,000 @return the name of number (e.g., “two hundred seventy four”) */ string int_name(int number) cfe2_ch05_p193_248.indd 211 10/26/10 6:14 PM 212 Chapter 5 Functions When you discover that you need a function, write a description of the parameter variables and return values. How can this function do its job? Let’s look at a simple case first. If the number is between 1 and 9, we need to compute "one" … "nine". In fact, we need the same computation again for the hundreds (two hundred). Using the stepwise decomposition process, we design another function for this simpler task. Again, rather than implementing the function, we first write the comment: /** Turns a digit into its English name. @param digit an integer between 1 and 9 @return the name of digit (“one” ... “nine”) */ string digit_name(int digit) a function may require simpler functions to carry out its work. This sounds simple enough to implement, using an if statement with nine branches. No further functions should be required for completing the digit_name function, so we will worry about the implementation later. Numbers between 10 and 19 are special cases. Let’s have a separate function teen_ name that converts them into strings "eleven", "twelve", "thirteen", and so on: /** Turns a number between 10 and 19 into its English name. @param number an integer between 10 and 19 @return the name of the number (“ten” ... “nineteen”) */ string teen_name(int number) Next, suppose that the number is between 20 and 99. Then we show the tens as "twenty", "thirty", …, "ninety". For simplicity and consistency, put that computation into a separate function: /** Gives the name of the tens part of a number between 20 and 99. @param number an integer between 20 and 99 @return the name of the tens part of the number (“twenty” ... “ninety”) */ string tens_name(int number) Now suppose the number is at least 20 and at most 99. If the number is evenly divisible by 10, we use tens_name, and we are done. Otherwise, we print the tens with tens_ name and the ones with digit_name. If the number is between 100 and 999, then we show a digit, the word "hundred", and the remainder as described previously. Here is the pseudocode of the algorithm: part = number (The part that still needs to be converted) name = "" (The name of the number) If part >= 100 name = name of hundreds in part + " hundred" Remove hundreds from part. If part >= 20 Append tens_name(part) to name. Remove tens from part. Else if part >= 10 Append teen_name(part) to name. part = 0 cfe2_ch05_p193_248.indd 212 10/26/10 6:14 PM 5.7 problem solving: stepwise refinement 213 If (part > 0) Append digit_name(part) to name. This pseudocode has a number of important improvements over the verbal description. It shows how to arrange the tests, starting with the comparisons against the larger numbers, and it shows how the smaller number is subsequently processed in further if statements. On the other hand, this pseudocode is vague about the actual conversion of the pieces, just referring to “name of hundreds” and the like. Furthermore, we were vague about spaces. As it stands, the code would produce strings with no spaces, twohundredseventyfour, for example. Compared to the complexity of the main problem, one would hope that spaces are a minor issue. It is best not to muddy the pseudocode with minor details. Now turn the pseudocode into real code. The last three cases are easy, because helper functions are already developed for them: if (part >= 20) { name = name + part = part % } else if (part >= { name = name + part = 0; } " " + tens_name(part); 10; 10) " " + teen_name(part); if (part > 0) { name = name + " " + digit_name(part); } Finally, let us tackle the case of numbers between 100 and 999. Because part < 1000, is a single digit, and we obtain its name by calling digit_name. Then we add the “hundred” suffix: part / 100 if (part >= 100) { name = digit_name(part / 100) + " hundred"; part = part % 100; } Now you have seen all the important building blocks for the int_name function. Here is the complete program: ch05/intname.cpp 1 2 3 4 5 6 7 8 9 10 11 12 cfe2_ch05_p193_248.indd 213 #include <iostream> #include <string> using namespace std; /** Turns a digit into its English name. @param digit an integer between 1 and 9 @return the name of digit (“one” ... “nine”) */ string digit_name(int digit) { 10/26/10 6:14 PM 214 Chapter 5 Functions 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 cfe2_ch05_p193_248.indd 214 if (digit == if (digit == if (digit == if (digit == if (digit == if (digit == if (digit == if (digit == if (digit == return ""; 1) 2) 3) 4) 5) 6) 7) 8) 9) return return return return return return return return return "one"; "two"; "three"; "four"; "five"; "six"; "seven"; "eight"; "nine"; } /** Turns a number between 10 and 19 into its English name. @param number an integer between 10 and 19 @return the name of the given number (“ten” ... “nineteen”) */ string teen_name(int { if (number == 10) if (number == 11) if (number == 12) if (number == 13) if (number == 14) if (number == 15) if (number == 16) if (number == 17) if (number == 18) if (number == 19) return ""; } number) return return return return return return return return return return "ten"; "eleven"; "twelve"; "thirteen"; "fourteen"; "fifteen"; "sixteen"; "seventeen"; "eighteen"; "nineteen"; /** Gives the name of the tens part of a number between 20 and 99. @param number an integer between 20 and 99 @return the name of the tens part of the number (“twenty” ... “ninety”) */ string tens_name(int { if (number >= 90) if (number >= 80) if (number >= 70) if (number >= 60) if (number >= 50) if (number >= 40) if (number >= 30) if (number >= 20) return ""; } number) return return return return return return return return "ninety"; "eighty"; "seventy"; "sixty"; "fifty"; "forty"; "thirty"; "twenty"; /** Turns a number into its English name. @param number a positive integer < 1,000 @return the name of the number (e.g. “two hundred seventy four”) */ string int_name(int number) { int part = number; // The part that still needs to be converted string name; // The return value 10/26/10 6:14 PM 5.7 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 problem solving: stepwise refinement 215 if (part >= 100) { name = digit_name(part / 100) + " hundred"; part = part % 100; } if (part >= 20) { name = name + part = part % } else if (part >= { name = name + part = 0; } " " + tens_name(part); 10; 10) " " + teen_name(part); if (part > 0) { name = name + " " + digit_name(part); } return name; } int main() { cout << "Please enter a positive integer: "; int input; cin >> input; cout << int_name(input) << endl; return 0; } program run Please enter a positive integer: 729 seven hundred twenty nine selF check 23. 24. 25. 26. 27. Explain how you can improve the int_name function so that it can handle arguments up to 9,999. Why does line 87 set part = 0? What happens when you call int_name(0)? How can you change the int_name function to handle this case correctly? Trace the function call int_name(72), as described in Programming Tip 5.4. Use the process of stepwise refinement to break down the task of printing the following table into simpler tasks. +-----+-----------+ | i | i * i * i | +-----+-----------+ | 1 | 1 | | 2 | 8 | ... | 20 | 8000 | +-----+-----------+ cfe2_ch05_p193_248.indd 215 10/26/10 6:14 PM 216 Chapter 5 Functions practice it programming tip 5.3 Now you can try these exercises at the end of the chapter: R5.12, P5.16, P5.19. keep Functions short There is a certain cost for writing a function. You need to design, code, and test the function. The function needs to be documented. You need to spend some effort to make the function reusable rather than tied to a specific context. To avoid this cost, it is always tempting just to stuff more and more code in one place rather than going through the trouble of breaking up the code into separate functions. It is quite common to see inexperienced programmers produce functions that are several hundred lines long. As a rule of thumb, a function that is so long that its code will not fit on a single screen in your development environment should probably be broken up. programming tip 5.4 tracing Functions When you design a complex set of functions, it is a good idea to carry out a manual walkthrough before entrusting your program to the computer. Take an index card, or some other piece of paper, and write down the function call that you want to study. Write the name of the function and the names and values of the parameter variables, like this: p int_name(number = 416) Then write the names and initial values of the function variables. Write them in a table, since you will update them as you walk through the code. int_name(number = 416) part name 416 "" We enter the test part >= 100. part / 100 is 4 and part % 100 is 16. digit_name(4) is easily seen to be "four". (Had digit_name been complicated, you would have started another sheet of paper to figure out that function call. It is quite common to accumulate several sheets in this way.) Now name has changed to name + " " + digit_name(part / 100) + " hundred", that is "four hundred", and part has changed to part % 100, or 16. int_name(number = 416) part name 416 "" 16 "four hundred" cfe2_ch05_p193_248.indd 216 10/26/10 6:14 PM 5.7 Now you enter the branch part values problem solving: stepwise refinement >= 10. teen_name(16) 217 is sixteen, so the variables now have the int_name(number = 416) part name 416 "" 16 "four hundred" 0 "four hundred sixteen" Now it becomes clear why you need to set part to 0 in line 87. Otherwise, you would enter the next branch and the result would be "four hundred sixteen six". Tracing the code is an effective way to understand the subtle aspects of a function. programming tip 5.5 stubs When writing a larger program, it is not always feasible to implement and test all functions at once. You often need to test a function that calls another, but the other function hasn’t yet been implemented. Then you can temporarily replace the missing function with a stub. A stub is a function that returns a simple value that is sufficient for testing another function. Here are examples of stub functions: /** Turns a digit into its English name. @param digit an integer between 1 and 9 @return the name of digit (“one” ... “nine”) */ string digit_name(int digit) { return "mumble"; } Stubs are incomplete functions that can be used for testing. /** Gives the name of the tens part of a number between 20 and 99. @param number an integer between 20 and 99 @return the tens name of the number (“twenty” ... “ninety”) */ string tens_name(int number) { return "mumblety"; } If you combine these stubs with the int_name function and test it with an argument of 274, you will get a result of "mumble hundred mumblety mumble", which indicates that the basic logic of the int_name function is working correctly. cfe2_ch05_p193_248.indd 217 10/26/10 6:14 PM 218 Chapter 5 Functions WorKeD exaMple 5.3 calculating a course grade This Worked Example uses stepwise refinement to solve the problem of converting a set of letter grades into an average grade for a course. 5.8 Variable scope and Global Variables the scope of a variable is the part of the program in which it is visible. It is possible to define the same variable name more than once in a program. When the variable name is used, you need to know to which definition it belongs. In this section, we discuss the rules for dealing with multiple definitions of the same name. A variable that is defined within a function is visible from the point at which it is defined until the end of the block in which it was defined. This area is called the scope of the variable. Consider the volume variables in the following example: double cube_volume(double side_length) { double volume = side_length * side_length * side_length; return volume; } int main() { double volume = cube_volume(2); cout << volume << endl; return 0; } Each volume variable is defined in a separate function, and their scopes do not overlap. In the same way that there can be a street named “Main Street” in different cities, a C++ program can have multiple variables with the same name. Available online at www.wiley.com/college/horstmann. cfe2_ch05_p193_248.indd 218 10/26/10 6:14 PM 5.8 Variable scope and Global Variables 219 It is not legal to define two variables with the same name in the same scope. For example, the following is not legal: int main() { double volume = cube_volume(2); double volume = cube_volume(10); // ERROR: cannot define another volume variable in this scope ... } However, you can define another variable with the same name in a nested block. Here, we define two variables called amount. double withdraw(double balance, double amount) { if (...) { double amount = 10; // Another variable named amount ... } ... } a variable in a nested block shadows a variable with the same name in an outer block. a local variable is defined inside a function. a global variable is defined outside a function. The scope of the parameter variable amount is the entire function, except inside the nested block. Inside the nested block, amount refers to the variable that was defined in that block. We say that the inner variable shadows the variable that is defined in the outer block. You should avoid this potentially confusing situation in the functions that you write, simply by renaming one of the variables. Variables that are defined inside functions are called local variables. C++ also supports global variables: variables that are defined outside functions. A global variable is visible to all functions that are defined after it. For example, the <iostream> header defines global variables cin and cout. Here is an example of a global variable: int balance = 10000; // A global variable void withdraw(double amount) { if (balance >= amount) { balance = balance - amount; } } int main() { withdraw(1000); cout << balance << endl; return 0; } avoid global variables in your programs. cfe2_ch05_p193_248.indd 219 The scope of the variable balance extends over both the withdraw and the main functions. Generally, global variables are not a good idea. When multiple functions update global variables, the result can be difficult to predict. Particularly in larger programs that are developed by multiple programmers, it is very important that the effect of each function be clear and easy to understand. You should avoid global variables in your programs. 10/26/10 6:14 PM 220 Chapter 5 Functions Consider this sample program: selF check 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int x; int mystery(int x) { int s = 0; for (int i = 0; i < x; i++) { int x = i + 1; s = s + x; } return x; } int main() { x = 4; int s = mystery(x); cout << s << endl; } 28. Which line defines a global variable? Which lines define local variables named x? Which lines are in the scope of the definition of x in line 2? Which variable is changed by the assignment in line 14? This program defines two variables with the same name whose scopes don’t overlap. What are they? 29. 30. 31. 32. practice it programming tip 5.6 Now you can try these exercises at the end of the chapter: R5.8, R5.9. avoid global Variables There are a few cases where global variables are required (such as cin and cout), but they are quite rare. Programs with global variables are difficult to maintain and extend because you can no longer view each function as a “black box” that simply receives arguments and returns a result. When functions modify global variables, it becomes more difficult to understand the effect of function calls. As programs get larger, this difficulty mounts quickly. Instead of using global variables, use function parameters to transfer information from one part of a program to another. 5.9 reference parameters If you want to write a function that changes the value of an argument, you must use a reference parameter in order to allow the change. We first explain why a different parameter type is necessary, then we show you the syntax for reference parameters. Consider a function that simulates withdrawing a given amount of money from a bank account, provided that sufficient funds are available. If the amount of money is insufficient, a $10 penalty is deducted instead. The function would be used as follows: double harrys_account = 1000; withdraw(harrys_account, 100); // Now harrys_account is 900 withdraw(harrys_account, 1000); // Insufficient funds. Now harrys_account is 890 cfe2_ch05_p193_248.indd 220 10/26/10 6:14 PM 5.9 reference parameters 221 Here is a first attempt: void withdraw(double balance, double amount) // Does not work { const double PENALTY = 10; if (balance >= amount) { balance = balance - amount; } else { balance = balance - PENALTY; } } But this doesn’t work. Let’s walk through the function call withdraw(harrys_account, 100)—see Figure 6. As the function starts, the parameter variable balance is created 1 and set to the same value as harrys_account, and amount is set to 100 2 . Then balance is modified 3 . Of course, that modification has no effect on harrys_account, because balance is a separate variable. When the function returns, balance is forgotten, and no money was withdrawn from harrys_account 4 . 1 Function call harrys_account = 1000 withdraw(harrys_account, 100); balance = amount = 2 Initializing function parameter variables harrys_account = 1000 balance = 1000 amount = 100 harrys_account = 1000 balance = 900 amount = 100 harrys_account = 1000 withdraw(harrys_account, 100); 3 About to return to the caller balance = balance - amount; 4 After function call Figure 6 cfe2_ch05_p193_248.indd 221 When balance and account are Value parameters 10/26/10 6:14 PM 222 Chapter 5 Functions main function withdraw function harrys_account = Reference parameter balance = amount = Value parameter Figure 7 Modifying a value parameter has no effect on the caller. a reference parameter refers to a variable that is supplied in a function call. 900 100 reference and Value parameters The parameter variable balance is called a value parameter, because it is initialized with the value of the supplied argument. All functions that we have written so far use value parameters. In this situation, though, we don’t just want balance to have the same value as harrys_account. We want balance to refer to the actual variable harrys_ account (or joes_account or whatever variable is supplied in the call). The contents of that variable should be updated. You use a reference parameter when you want to update a variable that was supplied in the function call. When we make balance into a reference parameter, then balance is not a new variable but a reference to an existing variable. Any change in balance is actually a change in the variable to which balance refers in that particular call. Figure 7 shows the difference between value and reference parameters. To indicate a reference parameter, you place an & after the type name. void withdraw(double& balance, double amount) The type double& is read “a reference to a double” or, more briefly, “double ref”. The withdraw function now has two parameter variables: one of type “double ref” and the other a value parameter of type double. The body of the function is unchanged. What has changed is the meaning of the assignments to the balance variable. The assignment balance = balance - amount; now changes the variable that was passed to the function (see Figure 8). A reference parameter for a bank balance is like an ATM card—it allows you to change the balance. In contrast, a value parameter can only tell you the balance. cfe2_ch05_p193_248.indd 222 10/26/10 6:14 PM 5.9 1 Function call reference parameters harrys_account = 223 1000 withdraw(harrys_account, 100); balance = amount = 2 Initializing function parameters harrys_account = 1000 withdraw(harrys_account, 100); balance = 3 About to return to the caller amount = 100 harrys_account = 900 balance = balance = balance - amount; 4 After function call Figure 8 amount = 100 harrys_account = 900 When balance is a reference parameter For example, the call withdraw(harrys_account, 100); modifies the variable harrys_account, and the call withdraw(sallys_account, 150); Modifying a reference parameter updates the variable that was supplied in the call. modifies the variable sallys_account. The argument for a reference parameter must always be a variable. It would be an error to supply a number: withdraw(1000, 500); // Error: argument for reference parameter must be a variable The reason is clear—the function modifies the reference parameter, but it is impossible to change the value of a number. For the same reason, you cannot supply an expression: withdraw(harrys_account + 150, 500); // Error: argument for reference parameter must be a variable ch05/account.cpp 1 2 3 4 cfe2_ch05_p193_248.indd 223 #include <iostream> using namespace std; 10/26/10 6:14 PM 224 Chapter 5 Functions 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 30 31 32 33 34 35 36 37 /** Withdraws the amount from the given balance, or withdraws a penalty if the balance is insufficient. @param balance the balance from which to make the withdrawal @param amount the amount to withdraw */ void withdraw(double& balance, double amount) { const double PENALTY = 10; if (balance >= amount) { balance = balance - amount; } else { balance = balance - PENALTY; } } int main() { double harrys_account = 1000; double sallys_account = 500; withdraw(harrys_account, 100); // Now harrys_account is 900 withdraw(harrys_account, 1000); // Insufficient funds // Now harrys_account is 890 withdraw(sallys_account, 150); cout << "Harry's account: " << harrys_account << endl; cout << "Sally's account: " << sallys_account << endl; p return 0; } program run Harry's account: 890 Sally's account: 350 selF check 33. 34. Would the withdraw function work correctly if the amount parameter was defined as double& instead of double? The following function is intended to transfer the given amount of money from one account to another. Supply the function parameters. void transfer(...) { if (balance1 >= amount) { balance1 = balance1 - amount; balance2 = balance2 + amount; } } 35. cfe2_ch05_p193_248.indd 224 Change the withdraw function so that it returns a bool value indicating whether the withdrawal was successful. Do not charge a penalty if the balance was insufficient. 10/26/10 6:14 PM 5.9 36. 37. Write a function minmax so that the call minmax(x, and y and b to the larger of x and y. What does this program print? reference parameters 225 y, a, b) sets a to the smaller of x void mystery(int& a, int& b) { a = a - b; b = b + a; a = b - a; } int main() { int x = 4; int y = 3; mystery(x, y); cout << x << " " << y << endl; } practice it programming tip 5.7 Now you can try these exercises at the end of the chapter: R5.17, P5.14. prefer return Values to reference parameters Some programmers use reference parameters as a mechanism for setting the result of a function. For example, void cube_volume(double side_length, double& volume) { volume = side_length * side_length * side_length; } However, this function is less convenient than our previous cube_volume function. It cannot be used in expressions such as cout << cube_volume(2). Use a reference parameter only when a function needs to update a variable. special topic 5.2 constant references It is not very efficient to have a value parameter that is a large object (such as a string value). Copying the object into a parameter variable is less efficient than using a reference parameter. With a reference parameter, only the location of the variable, not its value, needs to be transmitted to the function. You can instruct the compiler to give you the efficiency of a reference parameter and the meaning of a value parameter, by using a constant reference as shown below. The function void shout(const string& str) { cout << str << "!!!" << endl; } works exactly the same as the function void shout(string str) { cout << str << "!!!" << endl; } There is just one difference: Calls to the first function execute a bit faster. cfe2_ch05_p193_248.indd 225 10/26/10 6:14 PM 226 Chapter 5 Functions 5.10 recursive Functions (optional) A recursive function is a function that calls itself. This is not as unusual as it sounds at first. Suppose you face the arduous task of cleaning up an entire house. You may well say to yourself, “I’ll pick a room and clean it, and then I’ll clean the other rooms.” In other words, the cleanup task calls itself, but with a simpler input. Eventually, all the rooms will be cleaned. In C++, a recursive function uses the same principle. Here is a typical example. We want to print triangle patterns like this: [] [][] [][][] [][][][] Cleaning up a house can be solved recursively: Clean one room, then clean up the rest. Specifically, our task is to provide a function void print_triangle(int side_length) The triangle given above is printed by calling print_triangle(4). To see how recursion helps, consider how a triangle with side length 4 can be obtained from a triangle with side length 3. [] [][] [][][] [][][][] Print the triangle with side length 3. Print a line with four []. More generally, for an arbitrary side length: Print the triangle with side length – 1. Print a line with side length []. Here is the pseudocode translated to C++: void print_triangle(int side_length) { print_triangle(side_length - 1); for (int i = 0; i < side_length; i++) { cout << "[]"; } cout << endl; } There is just one problem with this idea. When the side length is 1, we don’t want to call print_triangle(0), print_triangle(-1), and so on. The solution is simply to treat this as a special case, and not to print anything when side_length is less than 1. void print_triangle(int side_length) { if (side_length < 1) { return; } print_triangle(side_length - 1); for (int i = 0; i < side_length; i++) { cout << "[]"; cfe2_ch05_p193_248.indd 226 10/26/10 6:14 PM 5.10 recursive Functions (optional) 227 } cout << endl; } a recursive computation solves a problem by using the solution of the same problem with simpler inputs. Look at the print_triangle function one more time and notice how utterly reasonable it is. If the side length is 0, nothing needs to be printed. The next part is just as reasonable. Print the smaller triangle and don’t think about why that works. Then print a row of []. Clearly, the result is a triangle of the desired size. There are two key requirements to make sure that the recursion is successful: • Every recursive call must simplify the task in some way. • There must be special cases to handle the simplest tasks directly. For a recursion to terminate, there must be special cases for the simplest inputs. The print_triangle function calls itself again with smaller and smaller side lengths. Eventually the side length must reach 0, and the function stops calling itself. Here is what happens when we print a triangle with side length 4. • The call printTriangle(4) calls printTriangle(3). • The call printTriangle(3) calls printTriangle(2). • The call printTriangle(2) calls printTriangle(1). • The call printTriangle(1) calls printTriangle(0). • The call printTriangle(0) returns, doing nothing. • The call printTriangle(1) prints []. • The call printTriangle(2) prints [][]. • The call printTriangle(3) prints [][][]. • The call print_triangle(4) prints [][][][]. The call pattern of a recursive function looks complicated, and the key to the successful design of a recursive function is not to think about it. This set of Russian dolls looks similar to the call pattern of a recursive function. ch05/triangle.cpp 1 2 3 4 5 6 7 8 cfe2_ch05_p193_248.indd 227 #include <iostream> using namespace std; /** */ Prints a triangle with a given side length. @param side_length the side length (number of [] along the base) 10/26/10 6:14 PM 228 Chapter 5 Functions 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void print_triangle(int side_length) { if (side_length < 1) { return; } print_triangle(side_length - 1); for (int i = 0; i < side_length; i++) { cout << "[]"; } cout << endl; } int main() { cout << "Enter the side length: "; int input; cin >> input; print_triangle(input); return 0; } program run Enter the side length: 10 [] [][] [][][] [][][][] [][][][][] [][][][][][] [][][][][][][] [][][][][][][][] [][][][][][][][][] [][][][][][][][][][] Recursion is not really necessary to print triangle shapes. You can use nested loops, like this: for (int i = 0; i < side_length; i++) { for (int j = 0; j < i; j++) { cout << "[]"; } cout << endl; } However, this pair of loops is a bit tricky. Many people find the recursive solution simpler to understand. selF check 38. Consider this slight modification of the print_triangle function: void print_triangle(int side_length) { if (side_length < 1) { return; } for (int i = 0; i < side_length; i++) { cout << "[]"; cfe2_ch05_p193_248.indd 228 10/26/10 6:14 PM 5.10 recursive Functions (optional) 229 } cout << endl; print_triangle(side_length - 1); } 39. What is the result of print_triangle(4)? Consider this recursive function: int mystery(int n) { if (n <= 0) { return 0; } return n + mystery(n - 1); } 40. What is mystery(4)? Consider this recursive function: int mystery(int n) { if (n <= 0) { return 0; } return mystery(n / 2) + 1; } 41. 42. practice it hoW to 5.2 What is mystery(20)? Write a recursive function for printing n box shapes [] in a row. The int_name function in Section 5.7 accepted arguments < 1,000. Using a recursive call, extend its range to 999,999. For example an input of 12,345 should return "twelve thousand three hundred forty five". Now you can try these exercises at the end of the chapter: R5.20, P5.28, P5.30. thinking recursively To solve a problem recursively requires a different mindset than to solve it by programming loops. In fact, it helps if you are, or pretend to be, a bit lazy and let others do most of the work for you. If you need to solve a complex problem, pretend that “someone else” will do most of the heavy lifting and solve the problem for all simpler inputs. Then you only need to figure out how you can turn the solutions with simpler inputs into a solution for the whole problem. To illustrate the recursive thinking process, consider the problem of Section 4.2, computing the sum of the digits of a number. We want to design a function digit_sum that computes the sum of the digits of an integer n. For example, digit_sum(1729) = 1 + 7 + 2 + 9 = 19. step 1 Break the input into parts that can themselves be inputs to the problem. In your mind, fix a particular input or set of inputs for the task that the key to finding a you want to solve, and think how you can simplify the inputs. Look recursive solution is for simplifications that can be solved by the same task, and whose reducing the input to a simpler input for solutions are related to the original task. the same problem. In the digit sum problem, consider how we can simplify an input such as n = 1729. Would it help to subtract 1? After all, digit_sum(1729) = digit_sum(1728) + 1. But consider n = 1000. There seems to be no obvious relationship between digit_sum(1000) and digit_sum(999). A much more promising idea is to remove the last digit, that is, compute n / 10 = 172. The digit sum of 172 is directly related to the digit sum of 1729. cfe2_ch05_p193_248.indd 229 10/26/10 6:14 PM 230 Chapter 5 Functions step 2 Combine solutions with simpler inputs into a solution of the original problem. In your mind, consider the solutions for the simpler inputs that you have discovered in Step 1. Don’t worry how those solutions are obtained. Simply have faith that the solutions are readily available. Just say to yourself: These are simpler inputs, so someone else will solve the problem for me. In the case of the digit sum task, ask yourself how you can obtain When designing a digit_sum(1729) if you know digit_sum(172). You simply add the last recursive solution, digit (9), and you are done. How do you get the last digit? As the do not worry about multiple nested calls. remainder n % 10. The value digit_sum(n) can therefore be obtained as digit_sum(n / 10) + n % 10 Don’t worry how digit_sum(n / smaller, and therefore it just works. step 3 10) is computed. The input is simply focus on reducing a problem to a slightly simpler one. Find solutions to the simplest inputs. A recursive computation keeps simplifying its inputs. To make sure that the recursion comes to a stop, you must deal with the simplest inputs separately. Come up with special solutions for them. That is usually very easy. Look at the simplest inputs for the digit_sum test: • A number with a single digit • 0 Random Fact 5.1 in 1971, Marcian e. “ted” hoff, an engineer at intel Corporation, was working on a chip for a manufacturer of electronic calculators. he realized that it would be a better idea to develop a general-purpose chip that could be programmed to interface with the keys and display of a calculator, rather than to do yet another custom design. thus, the microprocessor was born. at the time, its primary application was as a controller for calculators, washing machines, and the like. it took years for the computer industry to notice that a genuine central processing unit was now available as a single chip. hobbyists were the first to catch on. in 1974 the first computer kit, the altair 8800, was available from Mits electronics for about $350. the kit consisted of the microprocessor, a circuit board, a very small amount of memory, toggle switches, and a row of display lights. purchasers had to solder and assemble it, then program it in machine language through the toggle switches. it was not a big hit. cfe2_ch05_p193_248.indd 230 the explosive Growth of personal Computers the first big hit was the apple ii. it was a real computer with a keyboard, a monitor, and a floppy disk drive. When it was first released, users had a $3,000 machine that could play space invaders, run a primitive bookkeeping program, or let users program it in BasiC. the original apple ii did not even support lowercase letters, making it worthless for word processing. the breakthrough came in 1979 with a new spreadsheet program, VisiCalc. in a spreadsheet, you enter financial data and their relationships into a grid of rows and columns (see the figure at right). then you modify some of the data and watch in real time how the others change. For example, you can see how changing the mix of widgets in a manufacturing plant might affect estimated costs and profits. Middle managers in companies, who understood computers and were fed up with having to wait for hours or days to get their data runs back from the computing center, snapped up VisiCalc and the computer that was needed to run it. For them, the computer was a spreadsheet machine. the next big hit was the iBM personal Computer, ever after known as the pC. it was the first widely available personal computer that used intel’s 16-bit processor, the 8086, whose successors are still being used in personal computers today. the success of the pC was based not on any engineering breakthroughs but on the fact that it was easy to clone. iBM published the computer’s specifications in order to encourage third parties to develop plug-in cards. perhaps iBM did not foresee that functionally equivalent versions of their computer could be recreated by others, but a variety of pC clone vendors emerged, and ultimately iBM stopped selling personal computers. iBM never produced an operating system for its pCs—that is, the software that organizes the interaction between the user and the computer, starts application programs, and manages disk storage and other resources. instead, iBM offered customers the option of three separate operating systems. Most customers couldn’t care less about the operating system. 10/26/10 6:14 PM 5.10 recursive Functions (optional) 231 A number with a single digit is its own digit sum, so you can stop the recursion when n < 10, and return n in that case. Or, if you prefer, you can be even lazier. If n has a single digit, then digit_sum(n / 10) + n % 10 equals digit_sum(0) + n. You can simply terminate the recursion when n is zero. step 4 Implement the solution by combining the simple cases and the reduction step. Now you are ready to implement the solution. Make separate cases for the simple inputs that you considered in Step 3. If the input isn’t one of the simplest cases, then implement the logic you discovered in Step 2. Here is the complete digit_sum function: int digit_sum(int n) { // Special case for terminating the recursion if (n == 0) { return 0; } // General case return digit_sum(n / 10) + n % 10; } they chose the system that was able to launch most of the few applications that existed at the time. it happened to be Dos (Disk operating system) by Microsoft. Microsoft licensed the same operating system to other hardware vendors and encouraged software companies to write Dos applications. a huge number of useful application programs for pC-compatible machines was the result. pC applications were certainly useful, but they were not easy to learn. every vendor developed a different user interface: the collection of keystrokes, menu options, and settings that a user needed to master to use a software package effectively. Data exchange between applications was difficult, because each program used a different data format. the apple Macintosh changed all that in 1984. the designers of the Macintosh had the vision to supply an intuitive user interface with the computer and to force software developers to adhere to it. it took Microsoft and pC-compatible manufacturers years to catch up. Most personal computers are used for accessing information from online sources, entertainment, word processing, and home finance. some analysts predict that the personal computer will merge with the television set and cable network into an entertainment and information appliance. The Visicalc Spreadsheet Running on an Apple II cfe2_ch05_p193_248.indd 231 10/26/10 6:14 PM 232 Chapter 5 Functions Chapter suMMary understand the concepts of functions, arguments, and return values. • A function is a named sequence of instructions. • Arguments are supplied when a function is called. The return value is the result that the function computes. Be able to implement functions. • When defining a function, you provide a name for the function, a variable for each argument, and a type for the result. • Function comments explain the purpose of the function, the meaning of the parameter variables and return value, as well as any special requirements. describe the process of parameter passing. • Parameter variables hold the argument values supplied in the function call. pie(fruit) pie(fruit) describe the process of returning a value from a function. • The return statement terminates a function call and yields the function result. design and implement functions without return values. • Use a return type of void to indicate that a function does not return a value. develop functions that can be reused for multiple problems. • Eliminate replicated code or pseudocode by defining a function. • Design your functions to be reusable. Supply parameter variables for the values that can vary when the function is reused. apply the design principle of stepwise refinement. • Use the process of stepwise refinement to decompose complex tasks into simpler ones. • When you discover that you need a function, write a description of the parameter variables and return values. • A function may require simpler functions to carry out its work. cfe2_ch05_p193_248.indd 232 10/26/10 6:14 PM review exercises 233 determine the scope of variables in a program. • The scope of a variable is the part of the program in which it is visible. • A variable in a nested block shadows a variable with the same name in an outer block. • A local variable is defined inside a function. A global variable is defined outside a function. • Avoid global variables in your programs. describe how reference parameters work. • Modifying a value parameter has no effect on the caller. • A reference parameter refers to a variable that is supplied in a function call. • Modifying a reference parameter updates the variable that was supplied in the call. understand recursive function calls and implement simple recursive functions. • A recursive computation solves a problem by using the solution of the same problem with simpler inputs. • For a recursion to terminate, there must be special cases for the simplest inputs. • The key to finding a recursive solution is reducing the input to a simpler input for the same problem. • When designing a recursive solution, do not worry about multiple nested calls. Simply focus on reducing a problem to a slightly simpler one. reVieW exerCises r5.1 What is the difference between an argument and a return value? How many argu- ments can a function have? How many return values? r5.2 In which sequence are the lines of the program cube.cpp on page 198 executed, starting with the first line of main? r5.3 Give examples of the following, either from the C++ library or from the functions discussed in this chapter: a. A function with two double arguments and a double return value b. A function with a double argument and a double return value c. A function with two int arguments and an int return value d. A function with an int argument and a string return value e. A function with a string argument and no return value f. A function with a reference parameter and no return value g. A function with no arguments and an int return value cfe2_ch05_p193_248.indd 233 10/26/10 6:14 PM 234 Chapter 5 Functions r5.4 True or false? a. A function has exactly one return statement. b. A function has at least one return statement. c. A function has at most one return value. d. A function with return value void never has a return statement. e. When executing a return statement, the function exits immediately. f. A function with return value void must print a result. g. A function without arguments always returns the same value. r5.5 Consider these functions: double f(double x) { return g(x) + sqrt(h(x)); } double g(double x) { return 4 * h(x); } double h(double x) { return x * x + k(x) - 1; } double k(double x) { return 2 * (x + 1); } Without actually compiling and running a program, determine the results of the following function calls: a. double b. double c. double d. double e. double x1 = f(2); x2 = g(h(2)); x3 = k(g(2) + h(2)); x4 = f(0) + f(1) + f(2); x5 = f(-1) + g(-1) + h(-1) + k(-1); r5.6 Write pseudocode for a function that translates a telephone number with letters in it (such as 1-800-FLOWERS) into the actual phone number. Use the standard letters on a phone pad. r5.7 Design a function that prints a floating-point number as a currency value (with a $ sign and two decimal digits). a. Indicate how the programs ch02/volume2.cpp and ch04/invtable.cpp should change to use your function. b. What change is required if the programs should show a different currency, such as euro? r5.8 For each of the variables in the following program, indicate the scope. Then deter- mine what the program prints, without actually running the program. 1 2 3 4 cfe2_ch05_p193_248.indd 234 int a = 0; int b = 0; int f(int c) { 10/26/10 6:14 PM review exercises 235 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 30 31 int n = 0; a = c; if (n < c) { n = a + b; } return n; } int g(int c) { int n = 0; int a = c; if (n < f(c)) { n = a + b; } return n; } int main() { int i = 1; int b = g(i); cout << a + b + i << endl; return 0; } r5.9 We have seen three kinds of variables in C++: global variables, parameter variables, and local variables. Classify the variables of Exercise R5.8 according to these categories. r5.10 Use the process of stepwise refinement to describe the process of making scrambled eggs. Discuss what you do if you do not find eggs in the refrigerator. r5.11 How many parameters does the following function have? How many return values does it have? Hint: The C++ notions of “parameter ” and “return value” are not the same as the intuitive notions of “input” and “output”. void average(double& avg) { cout << "Please enter two numbers: "; double x; double y; cin >> x >> y; avg = (x + y) / 2; } r5.12 Perform a walkthrough of the int_name function with the following arguments: a. 5 b. 12 c. 21 d. 301 e. 324 f. 0 g. -2 cfe2_ch05_p193_248.indd 235 10/26/10 6:14 PM 236 Chapter 5 Functions r5.13 Consider the following function: int f(int n) { if (n <= 1) { return 1; } if (n % 2 == 0) // n is even { return f(n / 2); } else { return f(3 * n + 1); } } Perform traces of the computations f(1), f(2), f(3), f(4), f(5), f(6), f(7), f(8), f(9), and f(10). r5.14 Eliminate the global variable in the code at the end of Section 5.8 by a. passing the balance to the withdraw function and returning the updated balance. b. passing the balance as a reference parameter to the withdraw function. r5.15 Given the following functions, trace the function call print_roots(4). int i; int isqrt(int n) { i = 1; while (i * i <= n) { i++; } return i - 1; } void print_roots(int n) { for (i = 0; i <= n; i++) { cout << isqrt(i) << " "; } } How can you fix the code so that the output is as expected (that is, 0 1 1 1 2)? r5.16 Consider the following function that is intended to swap the values of two integers: void false_swap1(int& a, int& b) { a = b; b = a; } int main() { int x = 3; int y = 4; false_swap1(x, y); cout << x << " " << y << endl; return 0; } Why doesn’t the function swap the contents of x and y? How can you rewrite the function to work correctly? r5.17 Consider the following function that is intended to swap the values of two integers: void false_swap2(int a, int b) { cfe2_ch05_p193_248.indd 236 10/26/10 6:14 PM programming exercises 237 int temp = a; a = b; b = temp; } int main() { int x = 3; int y = 4; false_swap2(x, y); cout << x << " " << y << endl; return 0; } Why doesn’t the function swap the contents of x and y? How can you rewrite the function to work correctly? r5.18 The following function swaps two integers, without requiring a temporary variable: void { a b a } tricky_swap(int& a, int& b) = a - b; = a + b; = b - a; However, it fails in one important case, namely when calling tricky_swap(x, Explain what should happen and what actually happens. x). r5.19 Give pseudocode for a recursive function for printing all substrings of a given string. For example, the substrings of the string "rum" are "rum" itself, "ru", "um", "r", "u", "m", and the empty string. You may assume that all letters of the string are different. r5.20 Give pseudocode for a recursive function that sorts all letters in a string. For exam- ple, the string "goodbye" would be sorted into "bdegooy". proGraMMinG exerCises p5.1 The max function that is declared in the <algorithm> header returns the larger of its two arguments. Write a program that reads three floating-point numbers, uses the max function, and displays • the larger of the first two inputs. • the larger of the last two inputs. • the largest of all three inputs. p5.2 Write a function that computes the balance of a bank account with a given initial balance and interest rate, after a given number of years. Assume interest is compounded yearly. p5.3 Write the following functions and provide a program to test them. a. double smallest(double x, double y, double z), returning the smallest of the arguments b. double average(double x, double y, double z), returning the average of the arguments cfe2_ch05_p193_248.indd 237 10/26/10 6:14 PM 238 Chapter 5 Functions p5.4 Write the following functions: a. bool all_the_same(double x, double y, double z), returning true if the arguments are all the same b. bool all_different(double x, double y, double z), returning true if the arguments are all different c. bool sorted(double x, double y, double z), returning true if the arguments are sorted, with the smallest one coming first Provide a program that tests your functions. p5.5 Write the following functions: a. int first_digit(int n), returning the first digit of the argument b. int last_digit(int n), returning the last digit of the argument c. int digits(int n), returning the number of digits of the argument For example, first_digit(1729) is 1, last_digit(1729) is 9, and digits(1729) is 4. Provide a program that tests your functions. p5.6 Write a function string middle(string str) that returns a string containing the middle character in str if the length of str is odd, or the two middle characters if the length is even. For example, middle("middle") returns "dd". p5.7 Write a function string repeat(string str, int n) that returns the string str repeated n times. For example, repeat("ho", "hohoho". 3) returns p5.8 Write a function int count_vowels(string str) that returns a count of all vowels in the string str. Vowels are the letters a, e, i, o, and u, and their uppercase variants. p5.9 Write a function int count_words(string str) that returns a count of all words in the string str. Words are separated by spaces. For example, count_words("Mary had a little lamb") should return 5. p5.10 It is a well-known phenomenon that most people are easily able to read a text whose words have two characters flipped, provided the first and last letter of each word are not changed. For example: I dn’ot gvie a dman for a man taht can olny sepll a wrod one way. (Mrak Taiwn) Write a function string scramble(string word) that constructs a scrambled version of a given word, randomly flipping two characters other than the first and last one. Then write a program that reads words from cin and prints the scrambled words. p5.11 Write functions double sphere_volume(double r) double sphere_surface(double r) cfe2_ch05_p193_248.indd 238 10/26/10 6:14 PM programming exercises 239 double cylinder_volume(double r, double h) double cylinder_surface(double r, double h) double cone_volume(double r, double h) double cone_surface(double r, double h) that compute the volume and surface area of a sphere with radius r, a cylinder with a circular base with radius r and height h, and a cone with a circular base with radius r and height h. Then write a program that prompts the user for the values of r and h, calls the six functions, and prints the results. p5.12 Write functions double distance(double x1, double x2, double y1, double y2) void midpoint(double x1, double x2, double y1, double y2, double& xmid, double& ymid) void slope(double x1, double x2, double y1, double y2, bool& vertical, double& s) that compute the distance, midpoint, and slope of the line segment joining the points (x1, y1) and (x2, y2). The slope function should either set vertical to true and not set s, or set vertical to false and set s to the slope. p5.13 Write a function double read_double(string prompt) that displays the prompt string, followed by a space, reads a floating-point number in, and returns it. Here is a typical usage: salary = read_double("Please enter your salary:"); perc_raise = read_double("What percentage raise would you like?"); p5.14 Write a function void sort2(int& a, int& b) that swaps the values of a and b if a is greater than b and otherwise leaves a and b unchanged. For example, int u = 2; int v = 3; int w = 4; int x = 1; sort2(u, v); // u is still 2, v is still 3 sort2(w, x); // w is now 1, x is now 4 p5.15 Write a function sort3(int& a, int& b, int& c) that swaps its three arguments to arrange them in sorted order. For example, int v = 3; int w = 4; int x = 1; sort3(v, w, x); // v is now 1, w is now 3, x is now 4 Hint: Use sort2 of Exercise P5.14. p5.16 Enhance the int_name function so that it works correctly for values < 1,000,000,000. p5.17 Enhance the int_name function so that it works correctly for negative values and zero. Caution: Make sure the improved function doesn’t print 20 as "twenty zero". p5.18 For some values (for example, 20), the int_name function returns a string with a leading space (" twenty"). Repair that blemish and ensure that spaces are inserted only when necessary. Hint: There are two ways of accomplishing this. Either ensure that leading spaces are never inserted, or remove leading spaces from the result before returning it. cfe2_ch05_p193_248.indd 239 10/26/10 6:14 PM 240 Chapter 5 Functions p5.19 Write a program that prints a paycheck. Ask the program user for the name of the employee, the hourly rate, and the number of hours worked. If the number of hours exceeds 40, the employee is paid “time and a half”, that is, 150 percent of the hourly rate on the hours exceeding 40. Your check should look similar to that in the figure below. Use fictitious names for the payer and the bank. Be sure to use stepwise refinement and break your solution into several functions. Use the int_name function to print the dollar amount of the check. , Publishers Bank Minnesota 2000 Prince Blvd Jonesville, MN 55400 John Wiley & Sons, Inc. 111 River Street Hoboken, NJ 07030-5774 CHECK NUMBER 063331 Date 74-39 567390 311 Amount *******274.15 ****************************************** PAY 4659484 04/29/12 $ TWO HUNDRED SEVENTY FOUR AND 15 / 100 TO THE ORDER OF: JOHN DOE 1009 Franklin Blvd Sunnyvale, CA 95014 p5.20 Write a function that computes the weekday of a given date, using a formula known as Zeller’s congruence. Let d = the day of the month mm = the modified month (3 = March, ..., 12 = December, 13 = January, 14 = February) w = the weekday (0 = Monday, 1 = Tuesday, ..., 6 = Sunday) Then ( )  26 × ( mm + 1) 5 × ( year % 100 ) 21 × ( year 100 )  w = d + 5 + + + %7 10 4 4   Here, all ̸ denote integer division and % denotes the remainder operation. p5.21 Leap years. Write a function bool leap_year(int year) that tests whether a year is a leap year: that is, a year with 366 days. Leap years are necessary to keep the calendar synchronized with the sun because the earth revolves around the sun once every 365.25 days. Actually, that figure is not entirely precise, and for all dates after 1582 the Gregorian correction applies. Usually years that are divisible by 4 are leap years, for example 1996. However, years that are divisible by 100 (for example, 1900) are not leap years, but years that are divisible by 400 are leap years (for example, 2000). p5.22 Write a program that converts a Roman number such as MCMLXXVIII to its decimal number representation. Hint: First write a function that yields the numeric value of each of the letters. Then use the following algorithm: cfe2_ch05_p193_248.indd 240 10/26/10 6:14 PM programming exercises 241 total = 0 While the roman number string is not empty If the first character has a larger value than the second, or the string has length 1 Add value(first character) to total. Remove the character. Else Add value(second character) - value(first character) to total. Remove both characters. p5.23 In Exercise P3.23 you were asked to write a program to convert a number to its representation in Roman numerals. At the time, you did not know how to eliminate duplicate code, and as a consequence the resulting program was rather long. Rewrite that program by implementing and using the following function: string roman_digit(int n, string one, string five, string ten) That function translates one digit, using the strings specified for the one, five, and ten values. You would call the function as follows: roman_ones = roman_digit(n % 10, "I", "V", "X"); n = n / 10; roman_tens = roman_digit(n % 10, "X", "L", "C"); ... p5.24 Postal bar codes. For faster sorting of letters, the United States Postal Service encour- ages companies that send large volumes of mail to use a bar code denoting the zip code (see Figure 9). *************** ECRLOT CODE C671RTS2 JOHN DOE 1009 FRANKLIN BLVD SUNNYVALE CA 95014 – 5143 Figure 9 ** CO57 CO57 a postal Bar Code The encoding scheme for a five-digit zip code is shown in Figure 10. There are full-height frame bars on each side. The five encoded digits are followed by a check digit, which is computed as follows: Add up all digits, and choose the check digit to make the sum a multiple of 10. For example, the zip code 95014 has a sum of 19, so the check digit is 1 to make the sum equal to 20. Frame bars Digit 1 Digit 2 Digit 3 Digit 4 Digit 5 Check Digit Figure 10 encoding for Five-Digit Bar Codes cfe2_ch05_p193_248.indd 241 10/26/10 6:14 PM 242 Chapter 5 Functions Each digit of the zip code, and the check digit, is encoded according to the following table where 0 denotes a half bar and 1 a full bar. Digit Bar 1 (weight 7) Bar 2 (weight 4) Bar 3 (weight 2) Bar 4 (weight 1) Bar 5 (weight 0) 1 0 0 0 1 1 2 0 0 1 0 1 3 0 0 1 1 0 4 0 1 0 0 1 5 0 1 0 1 0 6 0 1 1 0 0 7 1 0 0 0 1 8 1 0 0 1 0 9 1 0 1 0 0 0 1 1 0 0 0 The digit can be easily computed from the bar code using the column weights 7, 4, 2, 1, 0. For example, 01100 is 0 × 7 + 1 × 4 + 1 × 2 + 0 × 1 × 0 × 0 = 6. The only exception is 0, which would yield 11 according to the weight formula. Write a program that asks the user for a zip code and prints the bar code. Use : for half bars, | for full bars. For example, 95014 becomes ||:|:::|:|:||::::::||:|::|:::||| p5.25 Write a program that reads in a bar code (with : denoting half bars and | denoting full bars) and prints out the zip code it represents. Print an error message if the bar code is not correct. p5.26 Write a program that prints instructions to get coffee, asking the user for input whenever a decision needs to be made. Decompose each task into a function, for example: void brew_coffee() { cout << "Add water to the coffee maker." << endl; cout << "Put a filter in the coffee maker." << endl; grind_coffee(); cout << "Put the coffee in the filter." << endl; ... } p5.27 Write a recursive function string reverse(string str) that computes the reverse of a string. For example, reverse("flow") should return "wolf". Hint: Reverse the substring starting at the second character, then add the first cfe2_ch05_p193_248.indd 242 10/26/10 6:14 PM programming exercises 243 character at the end. For example, to reverse "flow", first reverse "low" to "wol", then add the "f" at the end. p5.28 Write a recursive function bool is_palindrome(string str) that returns true if str is a palindrome, that is, a word that is the same when reversed. Examples of palindrome are “deed”, “rotor”, or “aibohphobia”. Hint: A word is a palindrome if the first and last letters match and the remainder is also a palindrome. p5.29 Use recursion to implement a function bool find(string str, string match) that tests whether match is contained in str: bool b = find("Mississippi", "sip"); // Sets b to true Hint: If str starts with match, then you are done. If not, consider the string that you obtain by removing the first character. p5.30 Use recursion to determine the number of digits in a number n. Hint: If n is < 10, it has one digit. Otherwise, it has one more digit than n / 10. p5.31 Use recursion to compute an, where n is a positive integer. Hint: If n is 1, then an = a. n = a × an–1. Otherwise, a engineering p5.32 The effective focal length f of a lens of thickness d that has surfaces with radii of curvature R1 and R2 is given by  1 ( n − 1) d  1 1 = ( n − 1)  − +  f nR1R2   R1 R2 where n is the refractive index of the lens medium. Write a function that computes f in terms of the other parameters. f engineering p5.33 A laboratory container is shaped like the frustum of a cone: R2 h R1 Write functions to compute the volume and surface area, using these equations: ( V = 31 π h R12 + R22 + R1R2 S = π ( R1 + R2 ) cfe2_ch05_p193_248.indd 243 ) ( R2 − R1 )2 + h2 + π R12 10/26/10 6:14 PM 244 Chapter 5 Functions engineering p5.34 In a movie theater, the angle q at which a viewer sees the picture on the screen depends on the distance x of the viewer from the screen. For a movie theater with the dimensions shown in the picture below, write a function that computes the angle for a given distance. x 24 ft. θ θ 6 ft. 8° Next, provide a more general function that works for theaters with arbitrary dimensions. engineering p5.35 Electric wire, like that in the photo, is a cylindrical conductor covered by an insulat- ing material. The resistance of a piece of wire is given by the formula R= ρL 4 ρL = A πd2 where ρ is the resistivity of the conductor, and L, A, and d are the length, crosssectional area, and diameter of the wire. The resistivity of copper is 1.678 × 10−8 Ω m. The wire diameter, d, is commonly specified by the American wire gauge (AWG), which is an integer, n. The diameter of an AWG n wire is given by the formula 36 − n d = 0.127 × 92 39 mm Write a C++ function double diameter(int wire_gauge) that accepts the wire gauge and returns the corresponding wire diameter. Write another C++ function double copper_wire_resistance(double length, int wire_gauge) that accepts the length and gauge of a piece of copper wire and returns the resistance of that wire. The resistivity of aluminum is 2.82 × 10−8 Ω m. Write a third C++ function double aluminum_wire_resistance(double length, int wire_gauge) that accepts the length and gauge of a piece of aluminum wire and returns the resistance of that wire. Write a C++ program to test these functions. engineering p5.36 The drag force on a car is given by FD = 1 2 ρ v ACD 2 where ρ is the density of air (1.23 kg̸m3), v is the velocity in units of m̸s, A is the projected area of the car (2.5 m2), and CD is the drag coefficient (0.2). cfe2_ch05_p193_248.indd 244 10/26/10 6:14 PM answers to self-Check Questions 245 The amount of power in watts required to overcome such drag force is P = FDv, and the equivalent horsepower required is Hp = P ̸ 746. Write a program that accepts a car’s velocity and computes the power in watts and in horsepower needed to overcome the resulting drag force. Note: 1 mph = 0.447 m̸s. ansWers to selF-CheCK Questions 1. The arguments are 3 and 2. The return value is 9. 2. The inner call to pow returns 22 = 4. Therefore, the outer call returns 42 = 16. 3. 3. 4. Users of the function can treat it as a black box. 5. 27. 6. 8 × 8 × 8 = 512. 7. double volume = pow(side_length, 3); return volume; 8. double square_area(double side_length) { double area = side_length * side_length; return area; } 9. (2 + 3) / (3 - 2) = 5 10. When the function is called, x is set to 4, y is set to 7, and z becomes 11. Then z is changed to 5.5, and that value is returned and printed. 11. When the function is called, x is set to 5. Then y is set to 25, and that value is returned and printed. 12. When the function is called, n is set to 5. Then n is incremented twice, setting it to 7. That value is returned and printed. 13. box_string("Hello"); box_string("World"); 14. The box_string function does not return a value. Therefore, you cannot use it in a << expression. 15. void shout(string str) { cout << str << "!!!" << endl; } 16. void box_string(string str) { int n = str.length(); for (int i = 0; i < n + 4; i++) { cout << "-"; } cout << endl; cout << "! " << str << " !" << endl; for (int i = 0; i < n + 4; i++) { cout << "-"; } cout << endl; } 17. void print_line(int count) { for (int i = 0; i < count; i++) { cfe2_ch05_p193_248.indd 245 10/26/10 6:14 PM 246 Chapter 5 Functions cout << "-"; } cout << endl; } void box_string(string str) { int n = str.length(); print_line(n + 2); cout << "!" << str << "!" << endl; print_line(n + 2); } 18. int total_pennies = round_to_pennies(total); int total_tax_pennies = round_to_pennies(total * tax_rate); where the function is defined as /** @param amount an amount in dollars and cents @return the amount in pennies, rounded to the nearest penny */ int round_to_pennies(double amount) { return static_cast<int>(100 * amount + 0.5); } 19. if (is_even(page)) ... where the function is defined as follows: bool is_even(int n) { return n % 2 == 0; } 20. Add parameter variables so you can pass the initial balance and interest rate to the function: double balance(double initial_balance, double rate, int years) { return initial_balance * pow(1 + rate / 100, years); } 21. int spaces = count_spaces(input); where the function is defined as follows: /** @param str any string @return the number spaces in str */ int count_spaces(string str) { int count = 0; for (int i = 0; i < str.length(); i++) { if (str.substr(i, 1) == " ") { count++; } } return count; } cfe2_ch05_p193_248.indd 246 10/26/10 6:14 PM answers to self-Check Questions 247 22. It is very easy to replace the space with any character. /** @param str any string @param ch a string of length 1 @return the number of times that ch occurs in str */ int count(string str, string ch) { int count = 0; for (int i = 0; i < str.length(); i++) { if (str.substr(i, 1) == ch) { count++; } } return count; } This is useful if you want to count other characters. For example, count(input, counts how many commas are in the input. 23. Change line 75 to ",") name = name + digit_name(part / 100) + " hundred"; In line 72, add the statement if (part >= 1000) { name = digit_name(part / 1000) + "thousand "; part = part % 1000; } In line 65, change 1000 to 10000 in the comment. 24. In the case of “teens”, we already have the last digit as part of the name. 25. Nothing is printed. One way of dealing with this case is to add the following statement before line 70. if (number == 0) { return "zero"; } 26. Here is the approximate trace: int_name(number = 72) part name 72 " seventy" 2 " seventy two" 27. 28. 29. 30. 31. cfe2_ch05_p193_248.indd 247 Note that the string starts with a blank space. Exercise P5.18 asks you to eliminate it. Here is one possible solution. Break up the task print table into print header and print body. The print header task calls print separator, prints the header cells, and calls print separator again. The print body task repeatedly calls print row and then calls print separator. 1. 2, 7. Lines 3, 4, 5, 6, 10, 11, but not 7 through 9. The global variable defined in line 1. 10/26/10 6:14 PM 248 Chapter 5 Functions 32. The variables s defined in lines 4 and 15. 33. Yes, but since the function does not modify the amount parameter variable, there is no need to do so. 34. void transfer(double& balance1, double& balance2, double amount) 35. bool withdraw(double& balance, double amount) { if (balance >= amount) { balance = balance - amount; return true; } else { return false; } } 36. void minmax(double x, double y, double& a, double& b) { if (x < y) { a = x; b = y; } else { a = y; b = x; } } 37. The program sets x to 1, then y to 4, then x to 3. It prints 3 4. 38. [][][][] [][][] [][] [] 39. 4 + 3 + 2 + 1 + 0 = 10 40. mystery(10) + 1 = mystery(5) + 2 = mystery(2) + 3 = mystery(1) + 4 = mystery(0) + 5 = 5 41. The idea is to print one [], then print n - 1 of them. void print_boxes(int n) { if (n == 0) { return; } cout << "[]"; print_boxes(n - 1); } 42. Simply add the following to the beginning of the function: if (part >= 1000) { return int_name(part / 1000) + " thousand " + int_name(part % 1000); } cfe2_ch05_p193_248.indd 248 10/26/10 6:14 PM Chapter 6 a r r ay s a n d VeCtors Chapter Goals to become familiar with using arrays and vectors to collect values to learn about common algorithms for processing arrays and vectors to write functions that receive and return arrays and vectors to be able to use two-dimensional arrays Chapter Contents 6.1 ArrAys 250 Syntax 6.1: defining an array 251 Common Error 6.1: Bounds errors 254 Programming Tip 6.1: Use arrays for sequences of related Values 255 Random Fact 6.1: an early Internet Worm 255 6.2 Common ArrAy Algorithms 256 Special Topic 6.1: sorting with the C++ library 263 Special Topic 6.2: a sorting algorithm 263 Special Topic 6.3: Binary search 264 6.3 ArrAys And FunCtions 265 Special Topic 6.4: Constant array parameters 269 6.4 Problem solving: AdAPting Algorithms 269 6.5 Problem solving: disCovering Algorithms by mAniPulAting PhysiCAl objeCts 274 6.6 two-dimensionAl ArrAys 278 Syntax 6.2: two-dimensional array definition 279 Common Error 6.2: omitting the Column size of a two-dimensional array parameter 284 Worked Example 6.2: a World population table 6.7 veCtors 284 Syntax 6.3: defining a Vector 285 Programming Tip 6.2: prefer Vectors over arrays 289 Random Fact 6.2: the First programmer 290 How To 6.1: Working with arrays 271 Worked Example 6.1: rolling the dice 249 cfe2_ch06_p249_306.indd 249 10/26/10 7:47 PM In many programs, you need to collect large numbers of values. In standard C++, you use arrays and vectors for this purpose. arrays are a fundamental structure of the C++ language. the standard C++ library provides the vector construct as a more convenient alternative when working with collections whose size is not fixed. In this chapter, you will learn about arrays, vectors, and common algorithms for processing them. 6.1 arrays We start this chapter by introducing the array data type. Arrays are the fundamental mechanism in C++ for collecting multiple values. In the following sections, you will learn how to define arrays and how to access array elements. 6.1.1 defining arrays Suppose you write a program that reads a sequence of values and prints out the sequence, marking the largest value, like this: 32 54 67.5 29 34.5 80 115 <= largest value 44.5 100 65 You do not know which value to mark as the largest one until you have seen them all. After all, the last value might be the largest one. Therefore, the program must first store all values before it can print them. Could you simply store each value in a separate variable? If you know that there are ten inputs, then you can store the values in ten variables value1, value2, value3, …, value10. However, such a sequence of variables is not very practical to use. You would have to write quite a bit of code ten times, once for each of the variables. To solve this problem, use an array: a structure for storing a sequence of values. values = 10 Figure 1 an array of size 10 250 cfe2_ch06_p249_306.indd 250 10/26/10 7:47 PM 6.1 arrays syntax 6.1 251 defining an array Size must be a constant. Element type Ok to omit size if initial values are given. Name Size double values[5] = { 32, 54, 67.5, 29, 34.5 }; Use brackets to access an element. Optional list of initial values values[i] = 0; The index must be ≥ 0 and < the size of the array. See page 254. Here we define an array that can hold ten values: double values[10]; This is the definition of a variable values whose type is “array of double”. That is, valstores a sequence of floating-point numbers. The [10] indicates the size of the array. (See Figure 1.) The array size must be a constant that is known at compile time. When you define an array, you can specify the initial values. For example, Use an array to collect a sequence of values of the same type. ues double values[] = { 32, 54, 67.5, 29, 34.5, 80, 115, 44.5, 100, 65 }; When you supply initial values, you don’t need to specify the array size. The compiler determines the size by counting the values. table 1 defining arrays ! cfe2_ch06_p249_306.indd 251 int numbers[10]; An array of ten integers. const int SIZE = 10; int numbers[SIZE]; It is a good idea to use a named constant for the size. int size = 10; int numbers[size]; Caution: In standard C++, the size must be a int squares[5] = { 0, 1, 4, 9, 16 }; An array of five integers, with initial values. int squares[] = { 0, 1, 4, 9, 16 }; You can omit the array size if you supply initial values. The size is set to the number of initial values. int squares[5] = { 0, 1, 4 }; If you supply fewer initial values than the size, the remaining values are set to 0. This array contains 0, 1, 4, 0, 0. string names[3]; An array of three strings. constant. This array definition will not work with all compilers. 10/26/10 7:47 PM 252 Chapter 6 arrays and Vectors 6.1.2 accessing array elements Individual elements in an array values are accessed by an integer index i, using the notation values[i]. The values stored in an array are called its elements. Each element has a position number, called an index. To access a value in the values array, you must specify which index you want to use. That is done with the [] operator: values[4] = 34.5; Now the element with index 4 is filled with 34.5. (See Figure 2). values = [0] [1] [2] [3] 34.5 [4] [5] [6] [7] [8] Figure 2 Filling an array element [9] You can display the contents of the element with index 4 with the following command: cout << values[4] << endl; an array element can be used like any variable. As you can see, the element values[4] can be used like any variable of type double. In C++, array positions are counted in a way that you may find surprising. If you look carefully at Figure 2, you will find that the fifth element was filled when we changed values[4]. In C++, the elements of arrays are numbered starting at 0. That is, the legal elements for the values array are values[0], the first element values[1], the second element values[2], the third element values[3], the fourth element values[4], the fifth element ... values[9], the tenth element an array index must be at least zero and less than the size of the array. You will see in Chapter 7 why this numbering scheme was chosen in C++. You have to be careful about index values. Trying to access a element that does not exist in the array is a serious error. For example, if values has twenty elements, you are not allowed to access values[20]. Attempting to access an element whose index is not within the valid index range is called a bounds error. The compiler does not catch this type of error. Even the running program generates no error message. If you make a bounds error, you silently read or overwrite another memory location. As a consequence, your program may have random errors, and it can even crash. Like a post office box that is identified by a box number, an array element is identified by an index. cfe2_ch06_p249_306.indd 252 10/26/10 7:47 PM 6.1 a bounds error, which occurs if you supply an invalid array index, can corrupt data or cause your program to terminate. arrays 253 The most common bounds error is the following: double values[10]; cout << values[10]; There is no values[10] in an array with ten elements—the legal index values range from 0 to 9. To visit all elements of an array, use a variable for the index. Suppose values has ten elements and the integer variable i takes values 0, 1, 2, and so on, up to 9. Then the expression values[i] yields each element in turn. For example, this loop displays all elements. for (int i = 0; i < 10; i++) { cout << values[i] << endl; } Note that in the loop condition the index is less than 10 because there is no element corresponding to values[10]. 6.1.3 partially Filled arrays An array cannot change size at run time. This is a problem when you don’t know in advance how many elements you need. In that situation, you must come up with a good guess on the maximum number of elements that you need to store. We call this quantity the capacity. For example, we may decide that we sometimes want to store more than ten values, but never more than 100: const int CAPACITY = 100; double values[CAPACITY]; With a partially filled array, you need to remember how many elements are filled. In a typical program run, only part of the array will be occupied by actual elements. We call such an array a partially filled array. You must keep a companion variable that counts how many elements are actually used. In Figure 3 we call the companion variable current_size. values = 32 54 67 24 current_size CAPACITY Not currently used Figure 3 . . . a partially Filled array With a partially filled array, keep a companion variable for the current size. cfe2_ch06_p249_306.indd 253 The following loop collects values and fills up the values array. int current_size = 0; double input; while (cin >> input) { if (current_size < CAPACITY) { values[current_size] = input; current_size++; 10/26/10 7:47 PM 254 Chapter 6 arrays and Vectors } } At the end of this loop, current_size contains the actual number of elements in the array. Note that you have to stop accepting inputs if the size of the array reaches the capacity. To process the gathered array elements, you again use the companion variable, not the capacity. This loop prints the partially filled array: p for (int i = 0; i < current_size; i++) { cout << values[i] << endl; } selF CheCk 1. 2. Define an array of integers containing the first five prime numbers. Assume the array primes has been initialized as described in Self Check 1. What is its contents after executing the following loop? for (int i = 0; i < 2; i++) { primes[4 - i] = primes[i]; } 3. Assume the array primes has been initialized as described in Self Check 1. What is its contents after executing the following loop? for (int i = 0; i < 5; i++) { primes[i]++; } 4. Given the definition const int CAPACITY = 10; double values[CAPACITY]; 5. 6. 7. Practice it Common error 6.1 write statements to put a zero into the elements of the array values with the lowest and the highest valid index. Given the array defined in Self Check 4, write a loop to print the elements of the array values in reverse order, starting with the last element. Define an array called words that can hold ten values of type string. Define an array containing two strings, "Yes", and "No". Now you can try these exercises at the end of the chapter: R6.1, R6.2, R6.6, P6.1. bounds errors Perhaps the most common error in using arrays is accessing a nonexistent element. double values[10]; values[10] = 5.4; // Error—values has 10 elements with subscripts 0 to 9 If your program accesses an array through an out-of-bounds subscript, there is no error message. Instead, the program will quietly (or not so quietly) corrupt some memory. Except for very short programs, in which the problem may go unnoticed, that corruption will make the cfe2_ch06_p249_306.indd 254 10/26/10 7:47 PM 6.1 arrays 255 program act unpredictably, and it can even cause the program to terminate. These are serious errors that can be difficult to detect. programming tip 6.1 use Arrays for sequences of related values Arrays are intended for storing sequences of values with the same meaning. For example, an array of test scores makes perfect sense: int scores[NUMBER_OF_SCORES]; But an array double personal_data[3]; that holds a person’s age, bank balance, and shoe size in positions 0, 1, and 2 is bad design. It would be tedious for the programmer to remember which of these data values is stored in which array location. In this situation, it is far better to use three separate variables. Random Fact 6.1 In november 1988, robert Morris, a student at Cornell University, launched a so-called virus program that infected about 6,000 computers connected to the Internet across the United states. tens of thousands of computer users were unable to read their e-mail or otherwise use their computers. all major universities and many high-tech companies were affected. (the Internet was much smaller then than it is now.) the particular kind of virus used in this attack is called a worm. the virus program crawled from one computer on the Internet to the next. the worm would attempt to connect to finger, a program in the UnIX operating system for finding information on a user who has an account on a particular computer on the network. like many programs in UnIX, finger was written in the C language. In C, as in C++, arrays have a fixed size. to store the user name to be looked up (say, walters@cs.sjsu.edu), the finger program allocated an array of 512 characters, under the assumption that nobody would ever provide such a long input. Unfortunately, C, like C++, does not check that an array index is less than the length of the array. If you write into an array using an index that is too large, you simply overwrite memory locations that belong to some other objects. In some versions of the finger cfe2_ch06_p249_306.indd 255 an early Internet Worm program, the programmer had been lazy and had not checked whether the array holding the input characters was large enough to hold the input. so the worm program purposefully filled the 512-character array with 536 bytes. the excess 24 bytes would overwrite a return address, which the attacker knew was stored just after the line buffer. When that function was finished, it didn’t return to its caller but to code supplied by the worm (see Figure 4). that code ran under the same superuser privileges as finger, allowing the worm to gain entry into the remote system. had the programmer who wrote finger been more conscientious, this particular attack would not be possible. In C++, as in C, all programmers must be very careful not to overrun array boundaries. one may well speculate what would possess the virus author to spend many weeks to plan the antisocial act of breaking into thousands of computers and disabling them. It appears that the break-in was fully intended by the author, but the disabling of the computers was a bug, caused by continuous reinfection. Morris was sentenced to 3 years probation, 400 hours of community service, and fined $10,000. In recent years, computer attacks have intensified and the motives have become more sinister. Instead of disabling computers, viruses often steal financial data or use the attacked computers for sending spam e-mail. sadly, many of these attacks continue to be possible because of poorly written programs that are susceptible to buffer overrun errors. 1 Before the attack Line buffer (512 bytes) Return address 2 After the attack Overrun buffer (536 bytes) Malicious code Return address Figure 4 a “Buffer overrun” attack 10/26/10 7:47 PM 256 Chapter 6 arrays and Vectors 6.2 Common array algorithms In the following sections, we discuss some of the most common algorithms for processing sequences of values. We present the algorithms so that you can use them with fully and partially filled arrays as well as vectors (which we will introduce in Section 6.7). When we use the expression size of values, you should replace it with a constant or variable that yields the number of elements in the array (or the expression values.size() if values is a vector.) 6.2.1 Filling This loop fills an array with zeroes: for (int i = 0; i < size of values; i++) { values[i] = 0; } Next, let us fill an array squares with the numbers 0, 1, 4, 9, 16, and so on. Note that the element with index 0 contains 02, the element with index 1 contains 12, and so on. for (int i = 0; i < size of squares; i++) { squares[i] = i * i; } 6.2.2 Copying to copy an array, use a loop to copy its elements to a new array. Consider two arrays: int squares[5] = { 0, 1, 4, 9, 16 }; int lucky_numbers[5]; Now suppose you want to copy all values from the first array to the second. The following assignment is an error: lucky_numbers = squares; // Error In C++, you cannot assign one array to another. Instead, you must use a loop to copy all elements: for (int i = 0; i < 5; i++) { lucky_numbers[i] = squares[i]; } squares = Figure 5 cfe2_ch06_p249_306.indd 256 0 1 4 9 16 [0] lucky_numbers = [1] [2] [3] [4] 0 1 4 9 16 [0] [1] [2] [3] [4] Copying elements to Copy an array 10/26/10 7:47 PM 6.2 Common array algorithms 257 6.2.3 sum and average Value You have already encountered this algorithm in Section 4.7.1. Here is the code for computing the sum of all elements in an array: double total = 0; for (int i = 0; i < size of values; i++) { total = total + values[i]; } To obtain the average, divide by the number of elements: double average = total / size of values; Be sure to check that the size is not zero. 6.2.4 Maximum and Minimum Use the algorithm from Section 4.7.4 that keeps a variable for the largest element that you have encountered so far. Here is the implementation for arrays: double largest = values[0]; for (int i = 1; i < size of values; i++) { if (values[i] > largest) { largest = values[i]; } } Note that the loop starts at 1 because we initialize largest with values[0]. To compute the smallest value, reverse the comparison. These algorithms require that the array contain at least one element. 6.2.5 element separators When separating elements, don’t place a separator before the first element. When you display the elements of a collection, you usually want to separate them, often with commas or vertical lines, like this: 1 | 4 | 9 | 16 | 25 Note that there is one fewer separator than there are numbers. Print the separator before each element except the initial one (with index 0): for (int i { if (i > { cout } cout << } cfe2_ch06_p249_306.indd 257 = 0; i < size of values; i++) 0) << " | "; values[i]; To print five elements, you need four separators. 10/26/10 7:47 PM 258 Chapter 6 arrays and Vectors 6.2.6 linear search a linear search inspects elements in sequence until a match is found. To search for a specific element, visit the elements and stop when you encounter the match. You often need to search for the position of an element so that you can replace or remove it. Visit all elements until you have found a match or you have come to the end of the array. Here we search for the position of the first element equal to 100. int pos = 0; bool found = false; while (pos < size of values && !found) { if (values[pos] == 100) { found = true; } else { pos++; } } If found is true, then pos is the position of the first match. 6.2.7 removing an element Consider a partially filled array values whose current size is stored in the variable current_size. Suppose you want to remove the element with index pos from values. If the elements are not in any particular order, that task is easy to accomplish. Simply overwrite the element to be removed with the last element, then decrement the variable tracking the size. (See Figure 6.) values[pos] = values[current_size - 1]; current_size--; The situation is more complex if the order of the elements matters. Then you must move all elements following the element to be removed to a lower index, then decrement the variable holding the size of the array. (See Figure 7.) for (int i = pos + 1; i < current_size; i++) { values[i - 1] = values[i]; } current_size--; current_size 32 54 67.5 29 34.5 80 115 44.5 100 65 [0] [1] [2] . . . [pos] Decrement after moving element [current_size - 1] 1 2 3 4 5 32 54 67.5 29 80 115 44.5 100 65 65 [0] [1] [2] . . . [pos] Decrement after moving elements [current_size - 1] Figure 6 Figure 7 removing an element in an Unordered array removing an element in an ordered array cfe2_ch06_p249_306.indd 258 10/26/10 7:47 PM 6.2 Common array algorithms 259 6.2.8 Inserting an element If the order of the elements does not matter, you can simply insert new elements at the end, incrementing the variable tracking the size. (See Figure 8.) For a partially filled array: if (current_size < CAPACITY) { current_size++; values[current_size - 1] = new_element; } Before inserting an element, move elements to the end of the array starting with the last one. It is more work to insert an element at a particular position in the middle of a sequence. First, increase the variable holding the current size. Next, move all elements above the insertion location to a higher index. Finally, insert the new element. Here is the code for a partially filled array: if (current_size < CAPACITY) { current_size++; for (int i = current_size - 1; i > pos; i--) { values[i] = values[i - 1]; } values[pos] = new_element; } Note the order of the movement: When you remove an element, you first move the next element down to a lower index, then the one after that, until you finally get to the end of the array. When you insert an element, you start at the end of the array, move that element to a higher index, then move the one before that, and so on until you finally get to the insertion location (see Figure 9). current_size 32 54 67.5 29 34.5 80 115 44.5 100 [0] [1] [2] . . . Insert new element here Incremented before inserting element [current_size - 1] 5 4 3 2 1 32 54 67.5 29 42 34.5 80 115 44.5 100 [0] [1] [2] . . . Insert new element here [pos] Incremented before moving elements [current_size - 1] Figure 8 Figure 9 Inserting an element in an Unordered array Inserting an element in an ordered array 6.2.9 swapping elements You often need to swap elements of an array. For example, the sorting algorithm in Special Topic 6.2 on page 263 sorts an array by repeatedly swapping elements. cfe2_ch06_p249_306.indd 259 10/26/10 7:47 PM 260 Chapter 6 arrays and Vectors Figure 10 swapping array elements 1 2 3 4 values = values = values = values = 32 54 67.5 29 34.5 32 54 67.5 29 34.5 32 29 67.5 29 34.5 32 29 67.5 54 34.5 [0] [1] [i] [2] Values to be swapped [3] [j] [4] [i] [j] temp = 54 temp = 54 temp = 54 [i] [j] [i] [j] Consider the task of swapping the elements at positions i and j of an array values. We’d like to set values[i] to values[j]. But that overwrites the value that is currently stored in values[i], so we want to save that first: Use a temporary variable when swapping two elements. double temp = values[i]; values[i] = values[j]; Now we can set values[j] to the saved value. values[j] = temp; Figure 10 shows the process. To swap two elements, you need a temporary variable. 6.2.10 reading Input If you know how many input values the user will supply, it is simple to place them into an array: double values[NUMBER_OF_INPUTS]; for (i = 0; i < NUMBER_OF_INPUTS; i++) { cin >> values[i]; } cfe2_ch06_p249_306.indd 260 10/26/10 7:47 PM 6.2 Common array algorithms 261 However, this technique does not work if you need to read an arbitrary number of inputs. In that case, add the values to an array until the end of the input has been reached. double values[CAPACITY]; int current_size = 0; double input; while (cin >> input) { if (current_size < CAPACITY) { values[current_size] = input; current_size++; } } Now values is a partially filled array, and the companion variable current_size is set to the number of input values. This loop discards any inputs that won’t fit in the array. A better approach would be to copy values to a new larger array when the capacity is reached (see Section 6.2.2). The following program solves the task that we set ourselves at the beginning of this chapter, to mark the largest value in an input sequence: ch06/largest.cpp 1 2 3 4 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 30 31 32 33 34 35 cfe2_ch06_p249_306.indd 261 #include <iostream> using namespace std; int main() { const int CAPACITY = 1000; double values[CAPACITY]; int current_size = 0; cout << "Please enter values, Q to quit:" << endl; double input; while (cin >> input) { if (current_size < CAPACITY) { values[current_size] = input; current_size++; } } double largest = values[0]; for (int i = 1; i < current_size; i++) { if (values[i] > largest) { largest = values[i]; } } for (int i = 0; i < current_size; i++) { cout << values[i]; if (values[i] == largest) { 10/26/10 7:47 PM 262 Chapter 6 arrays and Vectors 36 37 38 39 40 41 42 cout << " <== largest value"; } cout << endl; } return 0; } Program run Please enter values, Q to quit: 34.5 80 115 44.5 Q 34.5 80 115 <== largest value 44.5 selF CheCk 8. What is the output of the largest.cpp program with the following inputs? 20 10 20 Q 9. 10. Write a loop that counts how many elements in an array are equal to zero. Consider the algorithm to find the largest element in an array. Why don’t we initialize largest and i with zero, like this? double largest = 0; for (int i = 0; i < size of values; i++) { if (values[i] > largest) { largest = values[i]; } } 11. 12. When printing separators, we skipped the separator before the initial element. Rewrite the loop so that the separator is printed after each element, except for the last element. What is wrong with these statements for printing an array with separators? cout << values[0]; for (int i = 1; i < size of values; i++) { cout << ", " << values[i]; } 13. When searching for a match, we used a while loop, not a for loop. What is wrong with using this loop instead? for (pos = 0; pos < size of values && !found; pos++) { if (values[pos] == 100) { found = true; } } 14. cfe2_ch06_p249_306.indd 262 When inserting an element into an array, we moved the elements with larger index values, starting at the end. Why is it wrong to start at the insertion location, like this: 10/26/10 7:47 PM 6.2 Common array algorithms 263 for (int i = pos; i < size of values - 1; i++) { values[i + 1] = values[i]; } Practice it special topic 6.1 Now you can try these exercises at the end of the chapter: R6.10, R6.13, P6.6, P6.16. sorting with the C++ library You often want to sort the elements of an array or vector. Special Topic 6.2 shows you a sorting algorithm that is relatively simple but not very efficient. Efficient sorting algorithms are significantly more complex. Fortunately, the C++ library provides an efficient sort function. To sort an array a with size elements, call sort(a, a + size); To sort a vector values, make this call: sort(values.begin(), values.end()); To fully understand the curious syntax of these calls, you will need to know advanced C++ that is beyond the scope of this book. But don’t hesitate to call the sort function whenever you need to sort an array or vector. To use the sort function, include the <algorithm> header in your program. special topic 6.2 A sorting Algorithm A sorting algorithm rearranges the elements of a sequence so that they are stored in sorted order. Here is a simple sorting algorithm, called selection sort. Consider sorting the following array values: [0] [1] [2] [3] [4] 11 9 17 5 12 An obvious first step is to find the smallest element. In this case the smallest element is 5, stored in values[3]. You should move the 5 to the beginning of the array. Of course, there is already an element stored in values[0], namely 11. Therefore you cannot simply move values[3] into values[0] without moving the 11 somewhere else. You don’t yet know where the 11 should end up, but you know for certain that it should not be in values[0]. Simply get it out of the way by swapping it with values[3]: 5 9 17 11 12 Now the first element is in the correct place. In the foregoing figure, the darker color indicates the portion of the array that is already sorted. Next take the minimum of the remaining entries values[1]...values[4]. That minimum value, 9, is already in the correct place. You don’t need to do anything in this case, simply extend the sorted area by one to the right: 5 9 17 11 12 Repeat the process. The minimum value of the unsorted region is 11, which needs to be swapped with the first value of the unsorted region, 17: 5 cfe2_ch06_p249_306.indd 263 9 11 17 12 10/26/10 7:47 PM 264 Chapter 6 arrays and Vectors Now the unsorted region is only two elements long; keep to the same successful strategy. The minimum element is 12. Swap it with the first value, 17: 5 9 11 12 17 That leaves you with an unprocessed region of length 1, but of course a region of length 1 is always sorted. You are done. Here is the C++ code: for (int unsorted = 0; unsorted < size - 1; unsorted++) { // Find the position of the minimum int min_pos = unsorted; for (int i = unsorted + 1; i < size; i++) { if (values[i] < values[min_pos]) { min_pos = i; } } // Swap the minimum into the sorted area if (min_pos != unsorted) { double temp = values[min_pos]; values[min_pos] = values[unsorted]; values[unsorted] = temp; } } This algorithm is simple to understand, but it is not very efficient. Computer scientists have studied sorting algorithms extensively and discovered significantly better algorithms. The sort function of the C++ library provides one such algorithm—see Special Topic 6.1 on page 263. special topic 6.3 binary search When an array is sorted, there is a much faster search algorithm than the linear search of Section 6.2.6. Consider the following sorted array values: [0] [1] [2] [3] [4] [5] [6] [7] 1 5 8 9 12 17 20 32 We would like to see whether the value 15 is in the array. Let’s narrow our search by finding whether the value is in the first or second half of the array. The last point in the first half of the data set, values[3], is 9, which is smaller than the value we are looking for. Hence, we should look in the second half of the array for a match, that is, in the sequence: [0] [1] [2] [3] [4] [5] [6] [7] 1 5 8 9 12 17 20 32 Now the last value of the first half of this sequence is 17; hence, the value must be located in the sequence: [0] [1] [2] [3] [4] [5] [6] [7] 1 5 8 9 12 17 20 32 The last value of the first half of this very short sequence is 12, which is smaller than the value that we are searching, so we must look in the second half: [0] [1] [2] [3] [4] [5] [6] [7] 1 cfe2_ch06_p249_306.indd 264 5 8 9 12 17 20 32 10/26/10 7:47 PM 6.3 arrays and Functions 265 We still don’t have a match because 15 ≠ 17, and we cannot divide the subsequence further. If we wanted to insert 15 into the sequence, we would need to insert it just before values[5]. This search process is called a binary search, because we cut the size of the search in half in each step. That cutting in half works only because we know that the sequence of values is sorted. Here is an implementation in C++: bool found = false; int low = 0; int high = size - 1; int pos = 0; while (low <= high && !found) { pos = (low + high) / 2; // Midpoint of the subsequence if (values[pos] == searched_value) { found = true; } else if (values[pos] < searched_value) { low = pos + 1; } // else { high = pos - 1; } // Look in first half } if (found) { cout << "Found at position " << pos; } else { cout << "Not found. Insert before position " << pos; } Look in second half 6.3 arrays and Functions When passing an array to a function, also pass the size of the array. In this section, we will explore how to write functions that process arrays. A function that processes the values in an array needs to know the number of valid elements in the array. For example, here is a sum function that computes the sum of all elements in an array: double sum(double values[], int size) { double total = 0; for (int i = 0; i < size; i++) { total = total + values[i]; } return total; } Note the special syntax for array parameter variables. When writing an array parameter variable, you place an empty [] behind the parameter name. Do not specify the size of the array inside the brackets. When you call the function, supply both the name of the array and the size. For example, double double = { double NUMBER_OF_SCORES = 10; scores[NUMBER_OF_SCORES] 32, 54, 67.5, 29, 34.5, 80, 115, 44.5, 100, 65 }; total_score = sum(scores, NUMBER_OF_SCORES); You can also pass a smaller size to the function: double partial_score = sum(scores, 5); This call computes the sum of the first five elements of the scores array. Remember, the function has no way of knowing how many elements the array has. It simply relies on the size that the caller provides. cfe2_ch06_p249_306.indd 265 10/26/10 7:47 PM 266 Chapter 6 arrays and Vectors array parameters are always reference parameters. Array parameters are always reference parameters. (You will see the reason in Chapter 7.) Functions can modify array arguments, and those modifications affect the array that was passed into the function. For example, the following multiply function updates all elements in the array: void multiply(double values[], int size, double factor) { for (int i = 0; i < size; i++) { values[i] = values[i] * factor; } } a function’s return type cannot be an array. You do not use an & symbol to denote the reference parameter in this case. Although arrays can be function arguments, they cannot be function return types. If a function computes multiple values, the caller of the function must provide an array parameter variable to hold the result. void squares(int n, int result[]) { for (int i = 0; i < n; i++) { result[i] = i * i; } } When a function modifies the size of an array, it needs to tell its caller. a function that adds elements to an array needs to know its capacity. When a function changes the size of an array, it should indicate to the caller how many elements the array has after the call. The easiest way to do this is to return the new size. Here is an example—a function that adds input values to an array: int read_inputs(double inputs[], int capacity) { int current_size = 0; double input; while (cin >> input) { if (current_size < capacity) { inputs[current_size] = input; current_size++; } } return current_size; } Note that this function also needs to know the capacity of the array. Generally, a function that adds elements to an array needs to know is capacity. You would call this function like this: const int MAXIMUM_NUMBER_OF_VALUES = 1000; double values[MAXIMUM_NUMBER_OF_VALUES]; int current_size = read_inputs(values, MAXIMUM_NUMBER_OF_VALUES); // values is a partially filled array; the current_size variable specifies its size Alternatively, you can pass the size as a reference parameter. This is more appropriate for functions that modify an existing array: void append_inputs(double inputs[], int capacity, int& current_size) { double input; while (cin >> input) { cfe2_ch06_p249_306.indd 266 10/26/10 7:47 PM 6.3 arrays and Functions 267 if (current_size < capacity) { inputs[current_size] = input; current_size++; } } } This function is called as append_inputs(values, MAXIMUM_NUMBER_OF_VALUES, current_size); After the call, the current_size variable contains the new size. The following example program reads values from standard input, doubles them, and prints the result. The program uses three functions: • The read_inputs function fills an array with the input values. It returns the number of elements that were read. • The multiply function modifies the contents of the array that it receives, demonstrating that arrays are passed by reference. • The print function does not modify the contents of the array that it receives. ch06/functions.cpp 1 2 3 4 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 30 31 32 33 34 35 36 cfe2_ch06_p249_306.indd 267 #include <iostream> using namespace std; /** Reads a sequence of floating-point numbers. @param inputs an array containing the numbers @param capacity the capacity of that array @return the number of inputs stored in the array */ int read_inputs(double inputs[], int capacity) { int current_size = 0; cout << "Please enter values, Q to quit:" << endl; bool more = true; while (more) { double input; cin >> input; if (cin.fail()) { more = false; } else if (current_size < capacity) { inputs[current_size] = input; current_size++; } } return current_size; } /** Multiplies all elements of an array by a factor. @param values a partially filled array @param size the number of elements in values 10/26/10 7:47 PM 268 Chapter 6 arrays and Vectors 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 @param factor the value with which each element is multiplied */ void multiply(double values[], int size, double factor) { for (int i = 0; i < size; i++) { values[i] = values[i] * factor; } } /** Prints the elements of a vector, separated by commas. @param values a partially filled array @param size the number of elements in values */ void print(double values[], int size) { for (int i = 0; i < size; i++) { if (i > 0) { cout << ", "; } cout << values[i]; } cout << endl; } int main() { const int CAPACITY = 1000; double values[CAPACITY]; int size = read_inputs(values, CAPACITY); multiply(values, size, 2); print(values, size); return 0; } Program run Please enter values, Q to quit: 12 25 20 Q 24, 50, 40 selF CheCk 15. What happens if you call the sum function and you lie about the size? For example, calling double result = sum(values, 1000); 16. 17. 18. 19. cfe2_ch06_p249_306.indd 268 even though values has size 100. How do you call the squares function to compute the first five squares and store the result in an array numbers? Write a function that returns the first position of an element in an array, or –1 if the element is not present. Use the linear search algorithm of Section 6.2.6. Rewrite the read_inputs function so that the array size is a reference parameter, not a return value. Write the header for a function that appends two arrays into another array. Do not implement the function. 10/26/10 7:48 PM 6.4 Practice it special topic 6.4 problem solving: adapting algorithms 269 Now you can try these exercises at the end of the chapter: R6.14, P6.8, P6.12. Constant Array Parameters When a function doesn’t modify an array parameter, it is considered good style to add the const reserved word, like this: double sum(const double values[], int size) The const reserved word helps the reader of the code, making it clear that the function keeps the array elements unchanged. If the implementation of the function tries to modify the array, the compiler issues a warning. 6.4 problem solving: adapting algorithms By combining fundamental algorithms, you can solve complex programming tasks. In Section 6.2, you were introduced to a number of fundamental array algorithms. These algorithms form the building blocks for many programs that process arrays. In general, it is a good problem-solving strategy to have a repertoire of fundamental algorithms that you can combine and adapt. Consider this example problem: You are given the quiz scores of a student. You are to compute the final quiz score, which is the sum of all scores after dropping the lowest one. For example, if the scores are 8 7 8.5 9.5 7 4 10 then the final score is 50. We do not have a ready-made algorithm for this situation. Instead, consider which algorithms may be related. These include: • Calculating the sum (Section 6.2.3) • Finding the minimum value (Section 6.2.4) • Removing an element (Section 6.2.7) Now we can formulate a plan of attack that combines these algorithms. Find the minimum. Remove it from the array. Calculate the sum. Let’s try it out with our example. The minimum of [0] [1] [2] [3] [4] [5] [6] 8 7 8.5 9.5 7 4 10 is 4. How do we remove it? Now we have a problem. The removal algorithm in Section 6.2.7 locates the element to be removed by using the position of the element, not the value. But we have another algorithm for that: • Linear search (Section 6.2.6) cfe2_ch06_p249_306.indd 269 10/26/10 7:48 PM 270 Chapter 6 arrays and Vectors We need to fix our plan of attack: Find the minimum value. Find its position. Remove that position from the array. Calculate the sum. Will it work? Let’s continue with our example. We found a minimum value of 4. Linear search tells us that the value 4 occurs at position 5. [0] [1] [2] [3] [4] [5] [6] 8 We remove it: 7 8.5 9.5 7 10 [0] [1] [2] [3] [4] [5] 8 you should be familiar with the implementation of fundamental algorithms so that you can adapt them. 4 7 8.5 9.5 7 10 Finally, we compute the sum: 8 + 7 + 8.5 + 9.5 + 7 + 10 = 50. This walkthrough demonstrates that our strategy works. Can we do better? It seems a bit inefficient to find the minimum and then make another pass through the array to obtain its position. We can adapt the algorithm for finding the minimum to yield the position of the minimum. Here is the original algorithm: double smallest = values[0]; for (int i = 1; i < size of values; i++) { if (values[i] < smallest) { smallest = values[i]; } } When we find the smallest value, we also want to update the position: if (values[i] < smallest) { smallest = values[i]; smallest_position = i; } In fact, then there is no reason to keep track of the smallest value any longer. It is simply values[smallest_position]. With this insight, we can adapt the algorithm as follows: int smallest_position = 0; for (int i = 1; i < size of values; i++) { if (values[i] < values[smallest_position]) { smallest_position = i; } } With this adaptation, our problem is solved with the following strategy: Find the position of the minimum. Remove it from the array. Calculate the sum. In How To 6.1 on page 271, we develop a C++ program from this strategy. cfe2_ch06_p249_306.indd 270 10/26/10 7:48 PM 6.4 problem solving: adapting algorithms 271 The next section shows you a technique for discovering a new algorithm when none of the fundamental algorithms can be adapted to a task. selF CheCk 20. 21. 22. 23. 24. Section 6.2.7 has two algorithms for removing an element. Which of the two should be used to solve the task described in this section? It isn’t actually necessary to remove the minimum in order to compute the total score. Describe an alternative. How can you print the number of positive and negative values in a given array, using one or more of the algorithms in Section 4.7? How can you print all positive values in an array, separated by commas? Consider the following algorithm for collecting all matches in an array: int matches_size = 0; for (int i = 0; i < size of values; i++) { if (values[i] fulfills the condition) { matches[matches_size] = values[i]; matches_size++; } } How can this algorithm help you with Self Check 23? Practice it Now you can try these exercises at the end of the chapter: R6.15, R6.16. hoW to 6.1 working with Arrays When you process sequences of values, you usually need to use arrays. (In some very simple situations, you can process values as you read them in, without storing them.) This How To walks you through the necessary steps. Consider the example problem from Section 6.4: You are given the quiz scores of a student. You are to compute the final quiz score, which is the sum of all scores after dropping the lowest one. For example, if the scores are 8 7 8.5 9.5 7 5 10 then the final score is 50. step 1 Decompose your task into steps. You will usually want to break down your task into multiple steps, such as • Reading the data into an array. • Processing the data in one or more steps. • Displaying the results. In our sample problem, this yields the following pseudocode: Read inputs. Compute the final score. Display the score. When deciding how to process the data, you should be familiar with the array algorithms in Section 6.2. Many processing tasks can be solved by combining or adapting one or more of these algorithms. cfe2_ch06_p249_306.indd 271 10/26/10 7:48 PM 272 Chapter 6 arrays and Vectors The preceding section showed you how to decompose Compute the final score into fundamental algorithms: Find the position of the minimum. Remove it from the array. Calculate the sum. step 2 Determine functions, arguments, and return values for each step. Even though it may be possible to put all steps into the main function, this is rarely a good idea. The simplest and best approach is to make each nontrivial step into a separate function. In our example, we will implement four functions: • read_inputs • min_position • remove • sum • final_score For each function that processes an array, you will need to pass the array itself and the array size. For example, double sum(double values[], int size) If the function modifies the size, it needs to tell the caller what the new size is. The function can return the size, or it can use a reference parameter for the size. The second approach is a better choice for a function that modifies an existing array. We use the first approach with the function that reads input values. int read_inputs(double values[], int capacity) // Returns the size The remove function modifies the current_size parameter: void remove(double values[], int& current_size, int pos) At this point, you should document each function, like this: /** Removes an element from an array. The order of the elements is not preserved. @param values a partially filled array @param current_size the number of elements in values (will be reduced by 1 if the position is valid) @param pos the position of the element to be removed */ void remove(double values[], int& current_size, int pos) step 3 Implement each function, using helper functions when needed. We won’t show the code for the read_inputs function because you have seen it already. Let us implement the final_score function. It calls three helper functions, min_position, remove, and sum: /** Removes the smallest value of an array and returns the sum of the remaining values. @param values a partially filled array @param current_size the number of elements in values (will be reduced by 1) @return the sum of the values, excluding the minimum */ double final_score(double values[], int& current_size) { int pos = min_position(values, current_size); remove(values, current_size, pos); return sum(values, current_size); cfe2_ch06_p249_306.indd 272 10/26/10 7:48 PM 6.4 problem solving: adapting algorithms 273 } We discussed the algorithm for min_position in the preceding section: /** Gets the position of the minimum value from an array. @param values a partially filled array @param size the number of elements in values @return the position of the smallest element in values */ int min_position(double values[], int size) { int smallest_position = 0; for (int i = 1; i < size; i++) { if (values[i] < values[smallest_position]) { smallest_position = i; } } return smallest_position; } The remaining helper functions use the algorithms from Section 6.2. You will find the implementations in the book’s companion code. step 4 Consider boundary conditions for the functions that you are implementing Most functions that operate on arrays are a bit intricate, and you have to be careful that you handle both normal and exceptional situations. What happens with an empty array? An array that contains a single element? When no match is found? When there are multiple matches? Consider these boundary conditions and make sure that your functions work correctly. Here is one example of such a consideration. How do we know that the min_position function will be called with an array of size at least 1? (Recall that you must have at least one element in order to find the minimum.) That function is called from the final_score function. However, the final_score function could conceivably be called with an empty array. We need to either include a test or add a restriction to the function comment. We will opt for the latter and change the comment for the values parameter variable of the min_position function to @param values a partially filled array of size >= 1 Consider another potential problem. What if there are multiple matches? That means that a student had more than one test with a low score. The final_score function removes only one of the occurrences of that low score, and that is the desired behavior. step 5 Assemble and test the complete program. Now we are ready to combine the individual functions into a complete program. Before doing this, consider some test cases and their expected output: cfe2_ch06_p249_306.indd 273 test Case expected output Comment 8 7 8.5 9.5 7 5 10 50 See Step 1. 8 7 7 9 24 Only one instance of the low score should be removed. 8 0 After removing the low score, no score remains. (no inputs) error That is not a legal input. 10/26/10 7:48 PM 274 Chapter 6 arrays and Vectors This main function completes the solution (see ch06/scores.cpp). int main() { const int CAPACITY = 1000; double scores[CAPACITY]; int current_size = read_inputs(scores, CAPACITY); if (current_size == 0) { cout << "At least one score is required." << endl; } else { double score = final_score(scores, current_size); cout << "Final score: " << score << endl; } return 0; } Worked eXaMple 6.1 rolling the dice This Worked Example shows how to analyze a set of die tosses to see whether the die is “fair”. 6.5 problem solving: discovering algorithms by Manipulating physical objects In Section 6.4, you saw how to solve a problem by combining and adapting known algorithms. But what do you do when none of the standard algorithms is sufficient for your task? In this section, you will learn a technique for discovering algorithms by manipulating physical objects. Consider the following task. You are given an array whose size is an even number, and you are to switch the first and the second half. For example, if the array contains the eight numbers 9 13 21 4 11 7 1 3 then you should change it to 11 7 Use a sequence of coins, playing cards, or toys to visualize an array of values. 1 3 9 13 21 4 Many students find it quite challenging to come up with an algorithm. They may know that a loop is required, and they may realize that elements should be inserted (Section 6.2.8) or swapped (Section 6.2.9), but they do not have sufficient intuition to draw diagrams, describe an algorithm, or write down pseudocode. One useful technique for discovering an algorithm is to manipulate physical objects. Start by lining up some objects to denote an array. Coins, playing cards, or small toys are good choices. Available online at www.wiley.com/college/horstmann. cfe2_ch06_p249_306.indd 274 10/26/10 7:48 PM 6.5 problem solving: discovering algorithms by Manipulating physical objects 275 Manipulating physical objects can give you ideas for discovering algorithms. Here we arrange eight coins. Now let’s step back and see what we can do to change the order of the coins. We can remove a coin (Section 6.2.7): Visualizing the removal of an array element We can insert a coin (Section 6.2.8): Visualizing the insertion of an array element Or we can swap two coins (Section 6.2.9). Visualizing the swapping of two coins Go ahead—line up some coins and try out these three operations right now so that you get a feel for them. cfe2_ch06_p249_306.indd 275 10/26/10 7:48 PM 276 Chapter 6 arrays and Vectors Now how does that help us with our problem, switching the first and the second half of the array? Let’s put the first coin into place, by swapping it with the fifth coin. However, as C++ programmers, we will say that we swap the coins in positions 0 and 4: Next, we swap the coins in positions 1 and 5: Two more swaps, and we are done: Now an algorithm is becoming apparent: i=0 j = ... (we’ll think about that in a minute) while (don’t know yet) swap elements at positions i and j i++ j++ Where does the variable j start? When we have eight coins, the coin at position zero is moved to position 4. In general, it is moved to the middle of the array, or to position size / 2. cfe2_ch06_p249_306.indd 276 10/26/10 7:48 PM 6.5 problem solving: discovering algorithms by Manipulating physical objects 277 And how many iterations do we make? We need to swap all coins in the first half. That is, we need to swap size / 2 coins. The pseudocode is i=0 j = size / 2 while (i < size / 2) swap elements at positions i and j i++ j++ you can use paper clips as position markers or counters. selF CheCk It is a good idea to make a walkthrough of the pseudocode (see Section 4.2). You can use paper clips to denote the positions of the variables i and j. If the walkthrough is successful, then we know that there was no “off-by-one” error in the pseudocode. Self Check 25 asks you to carry out the walkthrough, and Exercise P6.7 asks you to translate the pseudocode to C++. Exercise R6.17 suggests a different algorithm for switching the two halves of an array, by repeatedly removing and inserting coins. Many people find that the manipulation of physical objects is less intimidating than drawing diagrams or mentally envisioning algorithms. Give it a try when you need to design a new algorithm! 25. 26. Walk through the algorithm that we developed in this section, using two paper clips to indicate the positions for i and j. Explain why there are no bounds errors in the pseudocode. Take out some coins and simulate the following pseudocode, using two paper clips to indicate the positions for i and j: i=0 j = size - 1 while (i < j) swap elements at positions i and j i++ j-- 27. What does the algorithm do? Consider the task of rearranging all values in an array so that the even numbers come first. Otherwise, the order doesn’t matter. For example, the array 1 4 14 2 1 3 5 6 23 could be rearranged to 4 2 14 6 1 5 3 23 1 28. 29. Practice it cfe2_ch06_p249_306.indd 277 Using coins and paperclips, discover an algorithm that solves this task by swapping elements, then describe it in pseudocode. Discover an algorithm for the task of Self Check 27 that uses removal and insertion of elements instead of swapping. Consider the algorithm in Section 4.7.4 that finds the largest element in a sequence of inputs—not the largest element in an array. Why is this algorithm better visualized by picking playing cards from a deck rather than arranging toy soldiers in a sequence? Now you can try these exercises at the end of the chapter: R6.17, R6.18, P6.7. 10/26/10 7:48 PM 278 Chapter 6 arrays and Vectors 6.6 two-dimensional arrays It often happens that you want to store collections of values that have a two-dimensional layout. Such data sets commonly occur in financial and scientific applications. An arrangement consisting of rows and columns of values is called a two-dimensional array, or a matrix. Let’s explore how to store the example data shown in Figure 11: the medal counts of the figure skating competitions at the 2010 Winter Olympics. Canada China Germany Korea Japan Russia United States Figure 11 Gold silver Bronze 1 1 0 1 0 0 1 0 1 0 0 1 1 1 1 0 1 0 1 1 0 Figure skating Medal Counts 6.6.1 defining two-dimensional arrays C++ uses an array with two subscripts to store a two-dimensional array. For example, here is the definition of an array with 7 rows and 3 columns, suitable for storing our medal count data: const int COUNTRIES = 7; const int MEDALS = 3; int counts[COUNTRIES][MEDALS]; You can initialize the array by grouping each row, as follows: int counts[COUNTRIES][MEDALS] = { { 1, 0, 1 }, { 1, 1, 0 }, { 0, 0, 1 }, { 1, 0, 0 }, { 0, 1, 1 }, { 0, 1, 1 }, { 1, 1, 0 } }; Just as with one-dimensional arrays, you cannot change the size of a two-dimensional array once it has been defined. cfe2_ch06_p249_306.indd 278 10/26/10 7:48 PM 6.6 syntax 6.2 two-dimensional arrays 279 two-dimensional array definition Element type Rows Columns Optional list of initial values int data[4][4] = { { { { { Name 16, 3, 2, 13 }, 5, 10, 11, 8 }, 9, 6, 7, 12 }, 4, 15, 14, 1 }, }; 6.6.2 accessing elements Individual elements in a two-dimensional array are accessed by using two subscripts, array[i][j]. To access a particular element in the two-dimensional array, you need to specify two subscripts in separate brackets to select the row and column, respectively (see Syntax 6.2 and Figure 12): int value = counts[3][1]; To access all values in a two-dimensional array, you use two nested loops. For example, the following loop prints all elements of counts. for (int i = 0; i < COUNTRIES; i++) { // Process the ith row for (int j = 0; j < MEDALS; j++) { // Process the jth column in the ith row cout << setw(8) << counts[i][j]; } cout << endl; // Start a new line at the end of the row } Column index [0][1][2] [0] Row index [1] [2] counts[3][1] [3] [4] [5] [6] Figure 12 accessing an element in a two-dimensional array cfe2_ch06_p249_306.indd 279 10/26/10 7:48 PM 280 Chapter 6 arrays and Vectors 6.6.3 Computing row and Column totals A common task is to compute row or column totals. In our example, the row totals give us the total number of medals won by a particular country. Finding the right index values is a bit tricky, and it is a good idea to make a quick sketch. To compute the total of row i, we need to visit the following elements: 0 row i MEDALS - 1 [i][0] [i][1] [i][2] As you can see, we need to compute the sum of counts[i][j], where j ranges from 0 to MEDALS - 1. The following loop computes the total: int total = 0; for (int j = 0; j < MEDALS; j++) { total = total + counts[i][j]; } Computing column totals is similar. Form the sum of from 0 to COUNTRIES - 1. counts[i][j], where i ranges column j [0][j] 0 [1][j] [2][j] [3][j] [4][j] [5][j] [6][j] COUNTRIES - 1 int total = 0; for (int i = 0; i < COUNTRIES; i++) { total = total + counts[i][j]; } cfe2_ch06_p249_306.indd 280 10/26/10 7:48 PM 6.6 two-dimensional arrays 281 6.6.4 two-dimensional array parameters a two-dimensional array parameter must have a fixed number of columns. When passing a two-dimensional array to a function, you must specify the number of columns as a constant with the parameter type. For example, this function computes the total of a given row: const int COLUMNS = 3; int row_total(int table[][COLUMNS], int row) { int total = 0; for (int j = 0; j < COLUMNS; j++) { total = total + table[row][j]; } return total; } This function can compute row totals of a two-dimensional array with an arbitrary number of rows, but the array must have 3 columns. You have to write a different function if you want to compute row totals of a two-dimensional array with 4 columns. To understand this limitation, you need to know how the array elements are stored in memory. Although the array appears to be two-dimensional, the elements are still stored as a linear sequence. Figure 13 shows how the counts array is stored, row by row. For example, to reach counts[3][1] the program must first skip past rows 0, 1, and 2 and then locate offset 1 in row 3. The offset from the start of the array is 3 × number of columns + 1 Now consider the row_total function. The compiler generates code to find the element table[i][j] by computing the offset i * COLUMNS + j The compiler uses the value that you supplied in the second pair of brackets when declaring the parameter: int row_total(int table[][COLUMNS], int row) Note that the first pair of brackets should be empty, just as with one-dimensional arrays. row 0 row 1 row 2 . . . counts = row 3 counts[3][1] Figure 13 cfe2_ch06_p249_306.indd 281 a two-dimensional array is stored as a sequence of rows 10/26/10 7:48 PM 282 Chapter 6 arrays and Vectors The row_total function did not need to know the number of rows of the array. If the number of rows is required, pass it as a variable, as in this example: int column_total(int table[][COLUMNS], int rows, int column) { int total = 0; for (int i = 0; i < rows; i++) { total = total + table[i][column]; } return total; } Working with two-dimensional arrays is illustrated in the following program. The program prints out the medal counts and the row totals. ch06/medals.cpp 1 2 3 4 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 30 31 32 33 34 35 36 37 38 39 40 41 42 cfe2_ch06_p249_306.indd 282 #include <iostream> #include <iomanip> #include <string> using namespace std; const int COLUMNS = 3; /** Computes the total of a row in a table. @param table a table with 3 columns @param row the row that needs to be totaled @return the sum of all elements in the given row */ double row_total(int table[][COLUMNS], int row) { int total = 0; for (int j = 0; j < COLUMNS; j++) { total = total + table[row][j]; } return total; } int main() { const int COUNTRIES = 7; const int MEDALS = 3; string countries[] = { "Canada", "China", "Germany", "Korea", "Japan", "Russia", "United States" }; int counts[COUNTRIES][MEDALS] = { 10/26/10 7:48 PM 6.6 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 { { { { { { { 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1 0 1 0 1 1 0 two-dimensional arrays 283 }, }, }, }, }, }, } }; cout << " Country Gold Silver Bronze Total" << endl; // Print countries, counts, and row totals for (int i = 0; i < COUNTRIES; i++) { cout << setw(15) << countries[i]; // Process the ith row for (int j = 0; j < MEDALS; j++) { cout << setw(8) << counts[i][j]; } int total = row_total(counts, i); cout << setw(8) << total << endl; } return 0; } Program run Country Canada China Germany Korea Japan Russia United States selF CheCk 30. 31. Gold 1 1 0 1 0 0 1 Silver 0 1 0 0 1 1 1 Bronze 1 0 1 0 1 1 0 Total 2 2 1 1 2 2 2 What results do you get if you total the columns in our sample data? Consider an 8 × 8 array for a board game: int board[8][8]; Using two nested loops, initialize the board so that zeroes and ones alternate, as on a checkerboard: 0 1 1 0 0 1 ... 1 0 32. 33. cfe2_ch06_p249_306.indd 283 0 1 0 1 0 1 1 0 1 0 1 0 0 1 0 1 0 1 1 0 1 0 1 0 Hint: Check whether i + j is even. Define a two-dimensional array for representing a tic-tac-toe board. The board has three rows and columns and contains strings "x", "o", and " ". Write an assignment statement to place an "x" in the upper-right corner of the tic-tac-toe board. 10/26/10 7:48 PM 284 Chapter 6 arrays and Vectors Which elements are on the diagonal joining the upper-left and the lower-right corners of the tic-tac-toe board? 34. Practice it Common error 6.2 Now you can try these exercises at the end of the chapter: R6.23, P6.19, P6.20. omitting the Column size of a two-dimensional Array Parameter When passing a one-dimensional array to a function, you specify the size of the array as a separate parameter variable: void print(double values[], int size) This function can print arrays of any size. However, for two-dimensional arrays you cannot simply pass the numbers of rows and columns as parameter variables: void print(double table[][], int rows, int cols) // NO! The function must know at compile time how many columns the two-dimensional array has. You must specify the number of columns with the array parameter variable. This number must be a constant: const int COLUMNS = 3; void print(const double table[][COLUMNS], int rows) // OK This function can print tables with any number of rows, but the column size is fixed. Worked eXaMple 6.2 A world Population table This Worked Example shows how to print world population data in a table with row and column headers, and with totals for each of the data columns. 6.7 Vectors a vector stores a sequence of values whose size can change. When you write a program that collects values from user input, you don’t always know how many values you will have. Unfortunately, the size of the array has to be known when the program is compiled. In Section 6.1.3, you saw how you can address this problem with partially filled arrays. The vector construct, which we discuss in the following sections, offers a more convenient solution. A vector collects a sequence of values, just like an array does, but its size can change. A vector expands to hold as many elements as needed. Available online at www.wiley.com/college/horstmann. cfe2_ch06_p249_306.indd 284 10/26/10 7:48 PM 6.7 syntax 6.3 Vectors 285 defining a Vector Element type Initial size vector<double> values(10); Name Use brackets to access an element. If you omit the size and the parentheses, the vector has initial size 0. values[i] = 0; The index must be ≥ 0 and < values.size(). 6.7.1 defining Vectors When you define a vector, you specify the type of the elements in angle brackets, like this: vector<double> values; You can optionally specify the initial size. For example, here is a definition of a vector whose initial size is 10: vector<double> values(10); If you define a vector without an initial size, it has size 0. While there would be no point in defining an array of size zero, it is often useful to have vectors with initial size zero, and then grow them as needed. In order to use vectors in your program, you need to include the <vector> header. table 2 defining Vectors cfe2_ch06_p249_306.indd 285 vector<int> numbers(10); A vector of ten integers. vector<string> names(3); A vector of three strings. vector<double> values; A vector of size 0. vector<double> values(); error: Does not define a vector. vector<int> numbers; for (int i = 1; i <= 10; i++) { numbers.push_back(i); } A vector of ten integers, filled with 1, 2, 3, ..., 10. vector<int> numbers(10); for (int i = 0; i < numbers.size(); i++) { numbers[i] = i + 1; } Another way of defining a vector of ten integers and filling it with 1, 2, 3, ..., 10. 10/26/10 7:48 PM 286 Chapter 6 arrays and Vectors Use the size member function to obtain the current size of a vector. You access the vector elements as values[i], just as you do with arrays. The size member function returns the current size of a vector. In a loop that visits all vector elements, use the size member function like this: for (int i = 0; i < values.size(); i++) { cout << values[i] << endl; } 6.7.2 Growing and shrinking Vectors Use the push_back member function to add more elements to a vector. Use pop_back to reduce the size. If you need additional elements, you use the push_back function to add an element to the end of the vector, thereby increasing its size by 1. The push_back function is a member function that you must call with the dot notation, like this: values.push_back(37.5); After this call, the vector value 37.5. 1 values Before push_back values = Figure 14 32 54 in Figure 14 has size 3, and 2 After push_back values = 2 32 54 37.5 values[2] contains the Size increased 3 New element added at end adding an element with push_back It is very common to start with an empty vector and use the push_back function to fill it. For example, vector<double> values; // Initially empty values.push_back(32); // Now values has size 1 and element 32 values.push_back(54); // Now values has size 2 and elements 32, 54 values.push_back(37.5); // Now values has size 3 and elements 32, 54, 37.5 Another common use for the push_back member function is to fill a vector with input values. vector<double> values; // Initially empty double input; while (cin >> input) { values.push_back(input); } Note how this input loop is much simpler than the one in Section 6.2.10. Another member function, pop_back, removes the last element of a vector, shrinking its size by one (see Figure 15): values.pop_back(); cfe2_ch06_p249_306.indd 286 10/26/10 7:48 PM 6.7 1 Before pop_back values = Figure 15 32 54 37.5 2 After pop_back values = 3 Vectors 32 54 287 Size decreased 2 This element to be removed removing an element with pop_back 6.7.3 Vectors and Functions Vectors can occur as function arguments and return values. You can use vectors as function arguments in exactly the same way as any other values. For example, the following function computes the sum of a vector of floatingpoint numbers: double sum(vector<double> values) { double total = 0; for (int i = 0; i < values.size(); i++) { total = total + values[i]; } return total; } Use a reference parameter to modify the contents of a vector. This function visits the vector elements, but it does not modify them. If your function modifies the elements, use a reference parameter. The following function multiplies all values of a vector with a given factor. void multiply(vector<double>& values, double factor) // Note the & { for (int i = 0; i < values.size(); i++) { values[i] = values[i] * factor; } } Some programmers use a constant reference (see Special Topic 5.2) for vector parameters that are not modified, for example: double sum(const vector<double>& values) // const & added for efficiency a function can return a vector. A function can return a vector. Again, vectors are no different from any other values in this regard. Simply build up the result in the function and return it. In this example, the squares function returns a vector of squares from 02 up to (n – 1)2: vector<int> squares(int n) { vector<int> result; for (int i = 0; i < n; i++) { result.push_back(i * i); } return result; } cfe2_ch06_p249_306.indd 287 10/26/10 7:48 PM 288 Chapter 6 arrays and Vectors As you can see, it is easy to use vectors with functions—there are no special rules to keep in mind. 6.7.4 Vector algorithms Most of the algorithms in Section 6.2 apply without change to vectors—simply replace size of values with values.size(). In this section, we discuss which of the algorithms are different for vectors. Copying As discussed in Section 6.2.2, you need an explicit loop to make a copy of an array. It is much easier to make a copy of a vector. You simply assign it to another vector. Consider this example: vector<int> squares; for (int i = 0; i < 5; i++) { squares.push_back(i * i); } vector<int> lucky_numbers; // Initially empty lucky_numbers = squares; // Now lucky_numbers contains the same elements as squares Finding Matches Section 6.2.6 shows you how to find the first match, but sometimes you want to have all matches. This is tedious with arrays, but simple using a vector that collects the matches. Here we collect all elements that are greater than 100: vector<double> matches; for (int i = 0; i < values.size(); i++) { if (values[i] > 100) { matches.push_back(values[i]); } } removing an element When you remove an element from a vector, you want to adjust the size of the vector by calling the pop_back member function. Here is the code for removing an element at [pos] when the order doesn’t matter. int last_pos = values.size() - 1; values[pos] = values[last_pos]; // Replace element at pos with last element values.pop_back(); // Delete last element When removing an element from an ordered vector, first move the elements, then reduce the size: for (int i = pos + 1; i < values.size(); i++) { values[i - 1] = values[i]; } values.pop_back(); p Inserting an element Inserting an element at the end of a vector requires no special code. Simply use the push_back member function. cfe2_ch06_p249_306.indd 288 10/26/10 7:48 PM 6.7 Vectors 289 When you insert an element in the middle, you still want to call push_back so that the size of the vector is increased. Use the following code: int last_pos = values.size() - 1; values.push_back(values[last_pos]); for (int i = last_pos; i > pos; i--) { values[i] = values[i - 1]; } values[pos] = new_element; selF CheCk 35. 36. 37. Define a vector of integers that contains the first five prime numbers (2, 3, 5, 7, and 11). Use push_back to add the elements. Answer Self Check 35 without using push_back. What is the contents of the vector names after the following statements? vector<string> names; names.push_back("Ann"); names.push_back("Bob"); names.pop_back(); names.push_back("Cal"); 38. 39. 40. 41. Suppose you want to store a set of temperature measurements that is taken every five minutes. Should you use a vector or an array? Suppose you want to store the names of the weekdays. Should you use a vector or an array of seven strings? Write the header for a function that appends two vectors, yielding a third vector. Do not implement the function. Consider this partially completed function that appends the elements of one vector to another. void append(vector<double>__ target, vector<double>__ source) { for (int i = 0; i < source.size(); i++) { target.push_back(source[i]); } } Specify whether the parameters should be value or reference parameters. Practice it programming tip 6.2 Now you can try these exercises at the end of the chapter: R6.11, R6.25, P6.26, P6.27. Prefer vectors over Arrays For most programming tasks, vectors are easier to use than arrays. Vectors can grow and shrink. Even if a vector always stays the same size, it is convenient that a vector remembers its size. For a beginner, the sole advantage of an array is the initialization syntax. Advanced programmers sometimes prefer arrays because they are a bit more efficient. Moreover, you need to know how to use arrays if you work with older programs. cfe2_ch06_p249_306.indd 289 10/26/10 7:48 PM 290 Chapter 6 arrays and Vectors Random Fact 6.2 Before pocket calculators and personal computers existed, navigators and engineers used mechanical adding machines, slide rules, and tables of logarithms and trigonometric functions to speed up computations. Unfortunately, the tables—for which values had to be computed by hand—were notoriously inaccurate. the mathematician Charles Babbage (1791–1871) had the insight that if a machine could be constructed that produced printed tables automatically, both calculation and typesetting errors could be avoided. Babbage set out to develop a machine for this purpose, which he called a Diference Engine because it the First programmer used successive differences to compute polynomials. For example, consider the function f (x) = x3. Write down the values for f (1), f (2), f (3), and so on. then take the diferences between successive values: 1 7 8 19 27 37 64 61 125 91 216 repeat the process, taking the difference of successive values in the second column, and then repeat once again: 1 7 8 12 19 27 6 18 37 64 6 24 61 125 6 30 91 216 Replica of Babbage’s Diference Engine now the differences are all the same. you can retrieve the function values by a pattern of additions—you need to know the values at the fringe of the pattern and the constant difference. you can try it out yourself: Write the highlighted numbers on a sheet of paper, and fill in the others by adding the numbers that are in the north and northwest positions. this method was very attractive, because mechanical addition machines had been known for some time. they consisted of cog wheels, with 10 cogs per wheel, to represent digits, and mechanisms to handle the carry from one digit to the next. Mechanical multiplication machines, on the other hand, were fragile and unreliable. Babbage built a successful prototype of the difference engine and, with his own money and government grants, proceeded to build the table-printing machine. however, because of funding problems and the difficulty of building the machine to the required precision, it was never completed. While working on the difference engine, Babbage conceived of a much grander vision that he called the Ana­ lytical Engine. the difference engine was designed to carry out a limited set of computations—it was no smarter than a pocket calculator is today. But Babbage realized that such a machine could be made programmable by storing programs as well as data. the internal storage of the analytical engine was to consist of 1,000 registers of 50 decimal digits each. programs and constants were to be stored on punched cards—a technique that was, at that time, commonly used on looms for weaving patterned fabrics. ada augusta, Countess of lovelace (1815–1852), the only child of lord Byron, was a friend and sponsor of Charles Babbage. ada lovelace was one of the first people to realize the potential of such a machine, not just for computing mathematical tables but for processing data that were not numbers. she is considered by many to be the world’s first programmer. Chapter sUMMary use arrays for collecting values. • Use an array to collect a sequence of values of the same type. • Individual elements in an array values are accessed by an integer index i, using the notation values[i]. cfe2_ch06_p249_306.indd 290 10/26/10 7:48 PM Chapter summary Overrun buffer (536 bytes) Malicious code 291 • An array element can be used like any variable. • An array index must be at least zero and less than the size of the array. • A bounds error, which occurs if you supply an invalid array index, can corrupt data or cause your program to terminate. • With a partially filled array, keep a companion variable for the current size. Return address be able to use common array algorithms. • • • • To copy an array, use a loop to copy its elements to a new array. When separating elements, don’t place a separator before the first element. A linear search inspects elements in sequence until a match is found. Before inserting an element, move elements to the end of the array starting with the last one. • Use a temporary variable when swapping two elements. implement functions that process arrays. • • • • • When passing an array to a function, also pass the size of the array. Array parameters are always reference parameters. A function’s return type cannot be an array. When a function modifies the size of an array, it needs to tell its caller. A function that adds elements to an array needs to know its capacity. be able to combine and adapt algorithms for solving a programming problem. • By combining fundamental algorithms, you can solve complex programming tasks. • You should be familiar with the implementation of fundamental algorithms so that you can adapt them. discover algorithms by manipulating physical objects. • Use a sequence of coins, playing cards, or toys to visualize an array of values. • You can use paper clips as position markers or counters. use two-dimensional arrays for data that is arranged in rows and columns. • Use a two-dimensional array to store tabular data. • Individual elements in a two-dimensional array are accessed by using two subscripts, array[i][j]. • A two-dimensional array parameter must have a fixed number of columns. cfe2_ch06_p249_306.indd 291 10/26/10 7:48 PM 292 Chapter 6 arrays and Vectors use vectors for managing collections whose size can change. • A vector stores a sequence of values whose size can change. • Use the size member function to obtain the current size of a vector. • Use the push_back member function to add more elements to a vector. Use pop_back to reduce the size. • Vectors can occur as function arguments and return values. • Use a reference parameter to modify the contents of a vector. • A function can return a vector. reVIeW eXerCIses r6.1 Write code that fills an array double values[10] with each set of values below. a. b. c. d. e. f. g. 1 0 1 0 1 0 0 2 2 4 0 4 1 1 3 4 9 0 9 0 2 4 6 16 0 16 1 3 5 8 25 0 9 0 4 6 10 36 0 7 1 0 7 12 49 0 4 0 1 8 14 64 0 9 1 2 9 16 81 0 11 0 3 10 18 100 0 1 4 r6.2 Consider the following array: int a[] = { 1, 2, 3, 4, 5, 4, 3, 2, 1, 0 }; What is the value of total after the following loops complete? a. int total = 0; for (int i = 0; i < 10; i++) { total = total + a[i]; } b. int total = 0; for (int i = 0; i < 10; i = i + 2) { total = total + a[i]; } c. int total = 0; for (int i = 1; i < 10; i = i + 2) { total = total + a[i]; } d. int total = 0; for (int i = 2; i <= 10; i++) { total = total + a[i]; } e. int total = 0; for (int i = 0; i < 10; i = 2 * i) { total = total + a[i]; } f. int total = 0; for (int i = 9; i >= 0; i--) { total = total + a[i]; } g. int total = 0; for (int i = 9; i >= 0; i = i - 2) { total = total + a[i]; } h. int total = 0; for (int i = 0; i < 10; i++) { total = a[i] - total; } r6.3 Consider the following array: int a[] = { 1, 2, 3, 4, 5, 4, 3, 2, 1, 0 }; cfe2_ch06_p249_306.indd 292 10/26/10 7:48 PM review exercises 293 What are the contents of the array a after the following loops complete? a. for b. for c. for d. for e. for f. for g. for h. for (int i = 1; i < 10; i++) { a[i] = a[i - 1]; } (int i = 9; i > 0; i--) { a[i] = a[i - 1]; } (int i = 0; i < 9; i++) { a[i] = a[i + 1]; } (int i = 8; i >= 0; i--) { a[i] = a[i + 1]; } (int i = 1; i < 10; i++) { a[i] = a[i] + a[i - 1]; } (int i = 1; i < 10; i = i + 2) { a[i] = 0; } (int i = 0; i < 5; i++) { a[i + 5] = a[i]; } (int i = 1; i < 5; i++) { a[i] = a[9 - i]; } r6.4 Write a loop that fills an array int values[10] with ten random numbers between 1 and 100. Write code for two nested loops that fill values with ten different random numbers between 1 and 100. r6.5 Write C++ code for a loop that simultaneously computes both the maximum and minimum of an array. r6.6 What is wrong with the following loop? int values[10]; for (int i = 1; i <= 10; i++) { values[i] = i * i; } Explain two ways of fixing the error. r6.7 What is an index of an array? What are the legal index values? What is a bounds error? r6.8 Write a program that contains a bounds error. Run the program. What happens on your computer? r6.9 Write a loop that reads ten numbers and a second loop that displays them in the opposite order from which they were entered. r6.10 Trace the flow of the element separator loop in Section 6.2.5 with the given example. Show two columns, one with the value of i and one with the output. r6.11 Trace the flow of the finding matches loop in Section 6.7.4, where values contains the elements 110 90 100 120 80. Show two columns, for i and matches. r6.12 Trace the flow of the linear search loop in Section 6.2.6, where values contains the elements 80 90 100 120 110. Show two columns, for pos and found. Repeat the trace when values contains 80 90 100 70. r6.13 Trace both mechanisms for removing an element described in Section 6.2.7. Use an array values with elements 110 90 100 120 80, and remove the element at index 2. r6.14 For the operations on partially filled arrays below, provide the header of a function. a. Sort the elements in decreasing order. b. Print all elements, separated by a given string. c. Count how many elements are less than a given value. d. Remove all elements that are less than a given value. e. Place all elements that are less than a given value in another array. Do not implement the functions. cfe2_ch06_p249_306.indd 293 10/26/10 7:48 PM 294 Chapter 6 arrays and Vectors r6.15 You are given two arrays denoting x- and y-coordinates of a set of points in the plane. For plotting the point set, we need to know the x- and y-coordinates of the smallest rectangle containing the points. y x How can you obtain these values from the fundamental algorithms in Section 6.2? r6.16 Solve the problem described in Section 6.4 by sorting the array first. How do you need to modify the algorithm for computing the total? r6.17 Solve the task described in Section 6.5 using an algorithm that removes and inserts elements instead of switching them. Write the pseudocode for the algorithm, assuming that functions for removal and insertion exist. Act out the algorithm with a sequence of coins and explain why it is less efficient than the swapping algorithm developed in Section 6.5. r6.18 Develop an algorithm for finding the most frequently occurring value in an array of numbers. Use a sequence of coins. Place paper clips below each coin that count how many other coins of the same value are in the sequence. Give the pseudocode for an algorithm that yields the correct answer, and describe how using the coins and paper clips helped you find the algorithm. r6.19 Give pseudocode for a function that rotates the elements of an array by one position, moving the initial element to the end of the array, like this: 2 3 5 7 11 13 3 5 7 11 13 2 r6.20 Give pseudocode for a function that removes all negative values from a partially filled array, preserving the order of the remaining elements. r6.21 Suppose values is a sorted partially filled array of integers. Give pseudocode that describes how a new value can be inserted in its proper position so that the resulting array stays sorted. r6.22 A run is a sequence of adjacent repeated values. Give pseudocode for computing the length of the longest run in an array. For example, the longest run in the array with elements 1 2 5 5 3 1 2 4 3 2 2 2 2 3 6 5 5 6 3 1 has length 4. r6.23 Write pseudocode for an algorithm that fills the first and last column as well as the first and last row of a two-dimensional array of integers with –1. r6.24 True or false? a. All elements of an array are of the same type. b. Arrays cannot contain strings as elements. cfe2_ch06_p249_306.indd 294 10/26/10 7:48 PM programming exercises 295 c. Two-dimensional arrays always have the same number of rows and columns. d. Elements of different columns in a two-dimensional array can have different types. e. A function cannot return a two-dimensional array. f. All array parameters are reference parameters. g. A function cannot change the dimensions of a two-dimensional array that is passed as a parameter. r6.25 How do you perform the following tasks with vectors in C++? a. Test that two vectors contain the same elements in the same order. b. Copy one vector to another. c. Fill a vector with zeroes, overwriting all elements in it. d. Remove all elements from a vector. r6.26 True or false? a. All elements of a vector are of the same type. b. Vector subscripts must be integers. c. Vectors cannot contain strings as elements. d. Vectors cannot use strings as subscripts. e. All vector parameters are reference parameters. f. A function cannot return a vector. g. A function cannot change the length of a vector that is a reference parameter. proGraMMInG eXerCIses P6.1 Write a program that initializes an array with ten random integers and then prints four lines of output, containing • Every element at an even index. • Every even element. • All elements in reverse order. • Only the first and last element. P6.2 Write array functions that carry out the following tasks for an array of integers: a. Swap the first and last element in an array. b. Shift all elements by one to the right and move the last element into the first position. For example, 1 4 9 16 25 would be transformed into 25 1 4 9 16. c. Replace all even elements with 0. d. Replace each element except the first and last by the larger of its two neighbors. e. Remove the middle element if the array length is odd, or the middle two elements if the length is even. f. Move all even elements to the front, otherwise preserving the order of the elements. g. Return the second-largest element in the array. cfe2_ch06_p249_306.indd 295 10/26/10 7:48 PM 296 Chapter 6 arrays and Vectors h. Return true if the array is currently sorted in increasing order. i. Return true if the array contains two adjacent duplicate values. j. Return true if the array contains duplicate values (which need not be adjacent). For each function, provide a test program. P6.3 Modify the largest.cpp program to mark both the smallest and the largest element. P6.4 Reimplement How To 6.1 without removing the minimum from the array of scores. Instead, compute the final score as the difference of the sum and the minimum of the scores. P6.5 Write a function void remove_min that removes the minimum value from a partially filled array without calling other functions. P6.6 Write a function that computes the alternating sum of all elements in an array. For example, if alternating_sum is called with an array containing then it computes 1 4 9 16 9 7 4 9 11 1 – 4 + 9 – 16 + 9 – 7 + 4 – 9 + 11 = –2 P6.7 Write a function that implements the algorithm developed in Section 6.5. P6.8 Write a function reverse that reverses the sequence of elements in an array. For example, if reverse is called with an array containing 1 4 9 16 then the array is changed to 11 9 4 7 9 7 9 16 4 9 9 11 4 1 P6.9 Write a function bool equals(int a[], int a_size, int b[], int b_size) that checks whether two arrays have the same elements in the same order. P6.10 Write a function bool same_set(int a[], int a_size, int b[], int b_size) that checks whether two vectors have the same elements in some order, ignoring duplicates. For example, the two arrays 1 4 9 11 11 16 9 7 7 9 16 4 9 11 and 4 1 would be considered identical. You will probably need one or more helper functions. P6.11 Write a function bool same_elements(int a[], int b[], int size) that checks whether two arrays have the same elements in some order, with the same multiplicities. For example, 1 cfe2_ch06_p249_306.indd 296 4 9 16 9 7 4 9 11 10/26/10 7:48 PM programming exercises 297 and 11 1 4 would be considered identical, but 1 4 11 11 9 9 16 16 9 9 7 7 4 4 9 9 11 and 7 9 16 4 1 4 9 would not. You will probably need one or more helper functions. P6.12 Write a function that removes duplicates from an array. For example, if remove_ duplicates is called with an array containing 1 4 then the array is changed to 9 16 9 7 4 9 11 1 4 9 16 7 11 Your function should have a reference parameter for the array size that is updated when removing the duplicates. P6.13 A run is a sequence of adjacent repeated values. Write a program that generates a sequence of 20 random die tosses and prints the die values, marking the runs by including them in parentheses, like this: 1 2 (5 5) 3 1 2 4 3 (2 2 2 2) 3 6 (5 5) 6 3 1 Use the following pseudocode: Set a Boolean variable in_run to false. For each valid index i in the array If in_run If values[i] is different from the preceding value Print ). in_run = false If not in_run If values[i] is the same as the following value Print (. in_run = true Print values[i]. If in_run, print ). P6.14 Write a program that generates a sequence of 20 random die tosses and that prints the die values, marking only the longest run, like this: 1 2 5 5 3 1 2 4 3 (2 2 2 2) 3 6 5 5 6 3 1 If there is more than one run of maximum length, mark the first one. P6.15 Write a program that generates a sequence of 20 random values between 0 and 99, prints the sequence, sorts it, and prints the sorted sequence. Use the sort function from the standard C++ library. P6.16 Write a program that produces ten random permutations of the numbers 1 to 10. To generate a random permutation, you need to fill an array with the numbers 1 to 10 so that no two elements have the same contents. You could do it by brute force, by cfe2_ch06_p249_306.indd 297 10/26/10 7:48 PM 298 Chapter 6 arrays and Vectors generating random values until you have a value that is not yet in the array. But that is inefficient. Instead, follow this algorithm: Make a second array and fill it with the numbers 1 to 10. Repeat 10 times Pick a random element from the second array. Remove it and append it to the permutation array. P6.17 It is a well-researched fact that men in a restroom generally prefer to maximize their distance from already occupied stalls, by occupying the middle of the longest sequence of unoccupied places. For example, consider the situation where all ten stalls are empty. _ _ _ _ _ _ _ _ _ _ The first visitor will occupy a middle position: _ _ _ _ X _ _ _ _ _ The next visitor will be in the middle of the empty area at the right. _ _ _ _ X _ _ X _ _ Given an array of bool values, where true indicates an occupied stall, find the position for the next visitor. Your computation should be placed in a function next_visitor(bool occupied[], int stalls) P6.18 In this assignment, you will model the game of Bulgarian Solitaire. The game starts with 45 cards. (They need not be playing cards. Unmarked index cards work just as well.) Randomly divide them into some number of piles of random size. For example, you might start with piles of size 20, 5, 1, 9, and 10. In each round, you take one card from each pile, forming a new pile with these cards. For example, the sample starting configuration would be transformed into piles of size 19, 4, 8, 9, and 5. The solitaire is over when the piles have size 1, 2, 3, 4, 5, 6, 7, 8, and 9, in some order. (It can be shown that you always end up with such a configuration.) In your program, produce a random starting configuration and print it. Then keep applying the solitaire step and print the result. Stop when the solitaire final configuration is reached. P6.19 Magic squares. An n × n matrix that is filled with the numbers 1, 2, 3, . . ., n2 is a magic square if the sum of the elements in each row, in each column, and in the two diagonals is the same value. 16 3 2 13 5 10 11 8 9 6 7 12 4 15 14 1 Write a program that reads in 16 values from the keyboard and tests whether they form a magic square when put into a 4 × 4 array. You need to test two features: 1. Does each of the numbers 1, 2, ..., 16 occur in the user input? 2. When the numbers are put into a square, are the sums of the rows, columns, and diagonals equal to each other? P6.20 Implement the following algorithm to construct magic n × n squares; it works only if n is odd. cfe2_ch06_p249_306.indd 298 10/26/10 7:48 PM programming exercises 299 Set row = n - 1, column = n / 2. For k = 1 ... n Place k at [row][column]. Increment row and column. If the row or column is n, replace it with 0. If the element at [row][column] has already been filled Set row and column to their previous value. Decrement row. Here is the 5 × 5 square that you get if you follow this method: 11 18 25 2 9 10 12 19 21 3 4 6 13 20 22 23 5 7 14 16 17 24 1 8 15 Write a program whose input is the number n and whose output is the magic square of order n if n is odd. P6.21 Write a function void bar_chart(double values[], int size) that displays a bar chart of the values in values, using asterisks, like this: ********************** **************************************** **************************** ************************** ************** You may assume that all values in values are positive. First figure out the maximum value in values. That value’s bar should be drawn with 40 asterisks. Shorter bars should use proportionally fewer asterisks. P6.22 Improve the bar_chart function of Exercise P6.21 to work correctly when values contains negative values. P6.23 Improve the bar_chart function of Exercise P6.21 by adding an array of captions for each bar. The output should look like this: Egypt France Japan Uruguay Switzerland ********************** **************************************** **************************** ************************** ************** P6.24 A theater seating chart is implemented as a two-dimensional array of ticket prices, like this: 10 10 10 10 10 10 20 20 30 cfe2_ch06_p249_306.indd 299 10 10 10 10 10 10 20 30 40 10 10 10 20 20 20 30 30 50 10 10 10 20 20 20 30 40 50 10 10 10 20 20 20 40 50 50 10 10 10 20 20 20 40 50 50 10 10 10 20 20 20 30 40 50 10 10 10 20 20 20 30 30 50 10 10 10 10 10 10 20 30 40 10 10 10 10 10 10 20 20 30 10/26/10 7:48 PM 300 Chapter 6 arrays and Vectors Write a program that prompts users to pick either a seat or a price. Mark sold seats by changing the price to 0. When a user specifies a seat, make sure it is available. When a user specifies a price, find any seat with that price. P6.25 Write a program that plays tic-tac-toe. The tic-tac-toe game is played on a 3 × 3 grid as in O X O The game is played by two players, who take turns. The first player marks moves with a circle, the second with a cross. The player who has formed a horizontal, vertical, or diagonal sequence of three marks wins. Your program should draw the game board, ask the user for the coordinates of the next mark, change the players after every successful move, and pronounce the winner. P6.26 Write a function vector<int> append(vector<int> a, vector<int> b) that appends one vector after another. For example, if a is 1 4 9 16 and b is then append returns the vector 1 4 9 7 4 9 11 9 16 9 7 4 9 11 P6.27 Write a function vector<int> merge(vector<int> a, vector<int> b) that merges two vectors, alternating elements from both vectors. If one vector is shorter than the other, then alternate as long as you can and then append the remaining elements from the longer vector. For example, if a is 1 4 9 16 and b is then merge returns the vector 1 9 9 7 4 9 11 4 7 9 4 16 9 11 P6.28 Write a function vector<int> merge_sorted(vector<int> a, vector<int> b) that merges two sorted vectors, producing a new sorted vector. Keep an index into each vector, indicating how much of it has been processed already. Each time, append the smallest unprocessed element from either vector, then advance the index. For example, if a is 1 4 9 16 and b is cfe2_ch06_p249_306.indd 300 10/26/10 7:48 PM programming exercises 4 7 9 9 11 then merge_sorted returns the vector 1 4 4 7 9 9 9 11 301 16 P6.29 Modify the ch06/image.cpp program in the book’s companion code to generate the image of a checkerboard. P6.30 Modify the ch06/animation.cpp program in the book’s companion code to show a rectangle that travels from the left of the image to the right and then back to the left. engineering P6.31 Sample values from an experiment often need to be smoothed out. One simple approach is to replace each value in an array with the average of the value and its two neighboring values (or one neighboring value if it is at either end of the array). Implement a function void smooth(double[] values, int size) that carries out this operation. You should not create another array in your solution. engineering P6.32 Sounds can be represented by an array of “sample val- ues” that describe the intensity of the sound at a point in time. The sound.cpp program in this book’s companion code reads a sound file (in WAV format), calls a function process for processing the sample values, and saves the sound file. Your task is to implement the process function by introducing an echo. For each sound value, add the value from 0.2 seconds ago. Scale the result so that no value is larger than 32767. engineering P6.33 You are given a two-dimensional array of values that give the height of a terrain at different points in a square. Write a function void flood_map(double heights[10][10], double water_level) that prints out a flood map, showing which of the points in the terrain would be flooded if the water level was the given value. In the flood map, print a * for each flooded point and a space for each point that is not flooded. Here is a sample map: * * * * * * * * cfe2_ch06_p249_306.indd 301 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 10/26/10 7:48 PM 302 Chapter 6 arrays and Vectors Then write a program that reads one hundred terrain height values and shows how the terrain gets flooded when the water level increases in ten steps from the lowest point in the terrain to the highest. engineering P6.34 Modify the ch06/image.cpp program in the book’s companion code to generate the image of a sine wave. Draw a line of pixels for every five degrees. engineering P6.35 Modify the ch06/animation.cpp program to show an animated sine wave. In the ith frame, shift the sine wave by 5 × i degrees. engineering P6.36 Write a program that models the movement of an object with mass m that is attached to an oscillating spring. When a spring is displaced from its equilibrium position by an amount x, Hooke’s law states that the restoring force is F = –kx where k is a constant that depends on the spring. (Use 10 N̸m for this simulation.) Unstretched spring x F Start with a given displacement x (say, 0.5 meter). Set the initial velocity v to 0. Compute the acceleration a from Newton’s law (F = ma) and Hooke’s law, using a mass of 1 kg. Use a small time interval Δt = 0.01 second. Update the velocity––it changes by aΔt. Update the displacement––it changes by vΔt. Every ten iterations, plot the spring displacement as a bar, where 1 pixel represents 1 cm. Modify the program ch06/image.cpp for creating an image. cfe2_ch06_p249_306.indd 302 10/26/10 7:48 PM answers to self-Check Questions 303 ansWers to selF-CheCk QUestIons 1. int primes[] = { 2, 3, 5, 7, 11 }; 2. 2, 3, 5, 3, 2 3. 3, 4, 6, 8, 12 4. values[0] = 0; values[CAPACITY - 1] = 0; 5. for (int i = SIZE - 1; i >= 0; i--) { cout << values[i] << endl; } 6. string words[10]; 7. string words[] = { "Yes", "No" }; 8. 20 <== largest value 10 20 <== largest value 9. int count = 0; for (int i = 0; i < size; i++) { if (values[i] == 0) { count++; } } 10. If all elements of values are negative, then the result is incorrectly computed as 0. 11. for (int i = 0; i < size; i++) { cout << values[i]; if (i < size - 1) { cout << " | "; } } 12. 13. 14. 15. Now you know why we set up the loop the other way. If the sequence has no elements, then a random value is printed. If there is a match, then pos is incremented before the loop exits. This loop sets all elements to values[pos]. The sum function will add up all the numbers in the values array and the next 900 numbers, yielding a random result. (Actually, there is the chance that the program doesn’t have the right to access all those numbers, in which case the operating system will terminate it.) 16. int numbers[5]; squares(5, numbers); 17. int find_first(double values[], int size, double searched_value) { for (int pos = 0; pos < size; pos++) { if (values[pos] == searched_value) { return pos; cfe2_ch06_p249_306.indd 303 10/26/10 7:48 PM 304 Chapter 6 arrays and Vectors } } return -1; } Note that the loop is simpler than that in Section 6.2.6 since we can simply return the position when a match is found. 18. void read_inputs(double inputs[], int capacity, int& size) { size = 0; double input; while (cin >> input) { if (size < capacity) { inputs[size] = input; size++; } } } 19. int append(double first[], int first_size, double second[], int second_size, double target[], int target_capacity) 20. 21. 22. 23. Note the following: • You must pass the sizes of the first and second arrays, so that the function knows how many elements to copy. • You must pass the capacity of the target, so that the function won’t write past the end. • The target array is a parameter variable—functions cannot return arrays. • The return type is int, so that the function can return the size of the target. (Alternatively, you could use a reference parameter int& target_size.) Use the first algorithm. The order of elements does not matter when computing the sum. Find the minimum value. Calculate the sum. Subtract the minimum value. Use the algorithm for counting matches (Section 4.7.2) twice, once for counting the positive values and once for counting the negative values. You need to modify the algorithm in Section 6.2.5. bool first = true; for (int i = 0; i < size of values; i++) { if (values[i] > 0)) { if (first) { first = false; } else { cout << ", "; } } cout << values[i]; } Note that you can no longer use i > 0 as the criterion for printing a separator. 24. Use the algorithm to collect all positive values in an array, then use the algorithm in Section 6.2.5 to print the array of matches. cfe2_ch06_p249_306.indd 304 10/26/10 7:48 PM answers to self-Check Questions 305 25. The paperclip for i assumes positions 0, 1, 2, 3. When i is incremented to 4, the condition i < size / 2 becomes false, and the loop ends. Similarly, the paperclip for j assumes positions 4, 5, 6, 7, which are the valid positions for the second half of the array. 26. It reverses the elements in the array. 27. Here is one solution. The basic idea is to move all odd elements to the end. Put one paper clip at the beginning of the array and one at the end. If the element at the first paper clip is odd, swap it with the one at the other paper clip and move that paper clip to the left. Otherwise, move the first paper clip to the right. Stop when the two paper clips meet. Here is the pseudocode: i=0 j = size - 1 While (i < j) If (a[i] is odd) Swap elements at positions i and j. j-Else i++ 28. Here is one solution. The idea is to remove all odd elements and move them to the end. The trick is to know when to stop. Nothing is gained by moving odd elements into the area that already contains moved elements, so we want to mark that area with another paper clip. i=0 moved = size While (i < moved) If (a[i] is odd) Remove the element at position i and add it at the end. moved-29. When you read inputs, you get to see values one at a time, and you can’t peek ahead. Picking cards one at a time from a deck of cards simulates this process better than looking at a sequence of items, all of whom are revealed. 30. You get the total number of gold, silver, and bronze medals in the competition. In our example, there are four of each. 31. for (int i = 0; i < 8; i++) { for (j = 0; j < 8; j++) { board[i][j] = (i + j) % 2; } } 32. string board[3][3]; 33. board[0][2] = "x"; cfe2_ch06_p249_306.indd 305 10/26/10 7:48 PM 306 Chapter 6 arrays and Vectors 34. board[0][0], board[1][1], board[2][2] 35. vector<int> primes; primes.push_back(2); primes.push_back(3); primes.push_back(5); primes.push_back(7); primes.push_back(11); 36. vector<int> primes(5); primes[0] = 2; primes[1] = 3; primes[2] = 5; primes[3] = 7; primes[4] = 11; 37. Ann, Cal 38. The problem doesn’t state how many measurements are taken. If the measurements go on for many months or years (which could well be the case in a scientific or industrial application), a vector is the better choice. If you know that the measurements are stored for a fixed period (say, one day), then an array will work equally well. 39. Because the numbers of weekdays doesn’t change, there is no disadvantage to using an array. 40. vector<double> append(vector<double> first, vector<double> second) Contrast this with the answer to Self Check 19. 41. target must be a reference parameter, source should be a value parameter. cfe2_ch06_p249_306.indd 306 10/26/10 7:48 PM Chapter 7 pointers Chapter Goals to be able to declare, initialize, and use pointers to understand the relationship between arrays and pointers to be able to convert between string objects and character pointers to become familiar with dynamic memory allocation and deallocation Chapter Contents 7.1 Defining anD Using Pointers 308 Syntax 7.1: pointer syntax 310 Common Error 7.1: Confusing pointers with the Data to Which they point 313 Programming Tip 7.1: Use a separate Definition for each pointer Variable 313 Special Topic 7.1: pointers and references 314 7.2 arrays anD Pointers 314 Special Topic 7.2: Using a pointer to step through an array 318 Programming Tip 7.2: program Clearly, not Cleverly 319 Common Error 7.2: returning a pointer to a local Variable 319 Special Topic 7.3: Constant pointers 320 7.3 C anD C++ strings 7.4 DynamiC memory alloCation 325 Syntax 7.2: Dynamic Memory allocation 325 Common Error 7.3: Dangling pointers 328 Common Error 7.4: Memory leaks 328 7.5 arrays anD VeCtors of Pointers 329 7.6 Problem solVing: Draw a PiCtUre 332 How To 7.1: Working with pointers 334 Worked Example 7.1: producing a Mass Mailing Random Fact 7.1: embedded systems 336 7.7 strUCtUres anD Pointers (oPtional) 336 320 Special Topic 7.4: Working with C strings 323 307 cfe2_ch07_p307_350.indd 307 10/28/10 8:42 PM in the game on the left, the spinner’s pointer moves to an item. a player follows the pointer and handles the item to which it points—by taking the ball or following the instructions written in the space. C++ also has pointers that can point to different values throughout a program run. pointers let you work with data whose locations change or whose size is variable. 7.1 Defining and Using pointers With a variable, you can access a value at a fixed location. With a pointer, the location can vary. This capability has many useful applications. Pointers can be used to share values among different parts of a program. Pointers allow allocation of values on demand. Furthermore, as you will see in Chapter 10, pointers are necessary for implementing programs that manipulate objects of multiple related types. In this chapter, you will learn how to define pointers and access the values to which they point. 7.1.1 Defining pointers Consider a person who wants a program for making bank deposits and withdrawals, but who may not always use the same bank account. By using a pointer, it is possible to switch to a different account without modifying the code for deposits and withdrawals. Let’s start with a variable for storing an account balance: double harrys_account = 0; a pointer denotes the location of a variable in memory. Now suppose that we want to write an algorithm that manipulates a bank account, but we anticipate that we may sometimes want to use harrys_account, sometimes another account. Using a pointer gives us that flexibility. A pointer tells you where a value is located, not what the value is. Here is the definition of a pointer variable. The pointer variable is initialized with the location (also called the address) of the variable harrys_account (see Figure 1): double* account_pointer = &harrys_account; 1 Like a pointer that points to different locations on a blackboard, a C++ pointer can point to different memory locations. 308 cfe2_ch07_p307_350.indd 308 10/28/10 8:42 PM 7.1 1 2 Defining and Using pointers double* account_pointer = &harrys_account 20300 harrys_account = 0 account_pointer = 20300 harrys_account = 0 account_pointer = 20312 account_pointer = &joint_account 20312 the type T* denotes a pointer to a variable of type T. the & operator yields the location of a variable. Point to memory at given address 20300 joint_account = figure 1 309 Point to memory at new address 0 pointers and Values in Memory The type double*, or “pointer to double”, denotes the location of a double variable. The & operator, also called the address operator, yields the location of a variable. Taking the address of a double variable yields a value of type double*. Thinking about pointers can be rather abstract, but you can use a simple trick to make it more tangible. Every variable in a computer program is located in a specific memory location. You don’t know where each variable is stored, but you can pretend you do. Let’s pretend that we know that harrys_account is stored in location 20300. (That is just a made-up value.) As shown in Figure 1, the value of harrys_account is 0, but the value of &harrys_account is 20300. The value of account_pointer is also 20300. In our diagrams, we will draw an arrow from a pointer to the location, but of course the computer doesn’t store arrows, just numbers. By using a pointer, you can switch to a different account at any time. To access a different account, simply change the pointer value: account_pointer = &joint_account; 2 7.1.2 accessing Variables through pointers the * operator accesses the variable to which a pointer points. When you have a pointer, you will want to access the variable to which it points. The * operator is used to read or update the variable to which a pointer points. When used with pointers, the * operator has no relationship with multiplication. In the C++ standard, this operator is called the indirection operator, but it is also commonly called the dereferencing operator. This statement makes an initial deposit into the account to which account_pointer points (see Figure 2): *account_pointer = 1000; 1 In other words, you can use *account_pointer in exactly the same way as harrys_account or joint_account. Which account is used depends on the value of the pointer. When the program executes this statement, it fetches the address stored in account_pointer. It then uses the variable at that address, as shown in Figure 2. cfe2_ch07_p307_350.indd 309 10/28/10 8:42 PM 310 Chapter 7 pointers 1 2 *account_pointer = 1000 20312 joint_account = 1000 account_pointer = 20312 joint_account = 1000 account_pointer = 20312 balance = 1000 balance = *account_pointer figure 2 Update memory at given address 20312 Read from memory pointer Variables Can be on either side of an assignment An expression such as *account_pointer can be on the left or the right of an assignment. When it occurs on the left, then the value on the right is stored in the location to which the pointer refers. When it occurs on the right, then the value is fetched from the location and assigned to the variable on the left. For example, the following statement reads the variable to which account_pointer currently points, and places its contents into the balance variable: balance = *account_pointer; 2 You can have *account_pointer on both sides of an assignment. The following statement withdraws $100: *account_pointer = *account_pointer - 100; Table 1 contains additional pointer examples. syntax 7.1 pointer syntax double account = 0; double* ptr = &account; The type of ptr is “pointer to double”. You should always initialize a pointer variable, either with a memory address or NULL. See page 328. The & operator yields a memory address. The * operator accesses the location to which ptr points. This statement changes account to 1000. *ptr = 1000 cout << *ptr; This statement reads from the location to which ptr points. cfe2_ch07_p307_350.indd 310 10/28/10 8:42 PM 7.1 Defining and Using pointers 311 table 1 pointer syntax examples Assume the following declarations: int m = 10; // Assumed to be at address 20300 int n = 20; // Assumed to be at address 20304 int* p = &m; expression Value p 20300 *p 10 &n 20304 The address of m. The value stored at that address. The address of n. Set p to the address of n. p = &n; *p Comment 20 The value stored at the changed address. Stores 20 into m. m = *p; m is an int value; p is an int* pointer. m = p; error &10 error You can only take the address of a variable. &p The address of p, perhaps 20308 This is the location of a pointer variable, not the location of an integer. double x = 0; p = &x; The types are not compatible. error p has type int*, &x has type double*. These types are incompatible. 7.1.3 initializing pointers With pointers, it is particularly important that you pay attention to proper initialization. When you initialize a pointer, be sure that the pointer and the memory address have the same type. For example, the following initialization would be an error: int balance = 1000; double* account_pointer = &balance; // Error! it is an error to use an uninitialized pointer. The address &balance is a pointer to an int value, that is, an expression of type int*. It is never legal to initialize a double* pointer with an int*. If you define a pointer variable without providing an initial variable, the pointer contains a random address. Using that random address is an error. In practice, your program will likely crash or mysteriously misbehave if you use an uninitialized pointer: double* account_pointer; // Forgot to initialize *account_pointer = 1000; // NO! account_pointer contains an unpredictable value cfe2_ch07_p307_350.indd 311 10/28/10 8:42 PM 312 Chapter 7 pointers the NULL pointer does not point to any object. There is a special value, NULL, that you should use to indicate a pointer that doesn’t point anywhere. If you define a pointer variable and are not ready to initialize it quite yet, set it to NULL. double* account_pointer = NULL; // Will set later You can later test whether the pointer is still NULL. If it is, don’t use it. if (account_pointer != NULL) { cout << *account_pointer; } // OK Trying to access data through a NULL pointer is illegal, and it will cause your program to terminate. The following program demonstrates the behavior of pointers. We execute the same withdrawal statement twice, but with different values for account_pointer. Each time, a different account is modified. ch07/accounts.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> using namespace std; int main() { double harrys_account = 0; double joint_account = 2000; double* account_pointer = &harrys_account; *account_pointer = 1000; // Initial deposit *account_pointer = *account_pointer - 100; // Withdraw $100 cout << "Balance: " << *account_pointer << endl; // Print balance // Change the pointer value account_pointer = &joint_account; // The same statements affect a different account *account_pointer = *account_pointer - 100; // Withdraw $100 cout << "Balance: " << *account_pointer << endl; // Print balance return 0; } Program run Balance: 900 Balance: 1900 p self CheCk 1. Consider this set of statements. What is printed? int a = 1; int b = 2; int* p = &a; cout << *p << endl; p = &b; cout << *p << endl; 2. Consider this set of statements. What is printed? int a = 1; int b = 2; cfe2_ch07_p307_350.indd 312 10/28/10 8:42 PM 7.1 int* int* *p = cout 3. Defining and Using pointers 313 p = &a; q = &b; *q; << a << " " << b << endl; Consider this set of statements. What is printed? int a = 15; int* p = &a; int* q = &a; cout << *p + *q << endl; 4. Consider this set of statements. What is printed? int a = 15; int* p = &a; int* q = &a; *p = *p + 10; cout << *q << endl; 5. Consider this set of statements. What is printed? int a = 15; int* p = &a; cout << *p << " " << p << endl; Practice it Common error 7.1 Now you can try these exercises at the end of the chapter: R7.1, R7.2, R7.4. Confusing Pointers with the Data to which they Point A pointer is a memory address—a number that tells where a value is located in memory. It is a common error to confuse the pointer with the variable to which it points: double* account_pointer = &joint_account; account_pointer =