0% found this document useful (0 votes)
83 views796 pages

C++ Book

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
83 views796 pages

C++ Book

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 796

Fundamentals of

C++
Programming
Richard L. Halterman
School of Computing
Southern Adventist University

August 20, 2023


Copyright © 2008–2023 Richard L. Halterman. All rights reserved.
i

Contents

1 The Context of Software Development 1


1.1 Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Development Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Learning Programming with C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2 Writing a C++ Program 7


2.1 General Structure of a Simple C++ Program . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Editing, Compiling, and Running the Program . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3 Variations of our simple program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.4 Template for simple C++ programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.5 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3 Values and Variables 15


3.1 Integer Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2 Variables and Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.3 Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.4 Additional Integer Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.5 Floating-point Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.6 Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.7 Other Numeric Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.8 Characters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.9 Enumerated Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.10 Type Inference with auto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.11 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

4 Expressions and Arithmetic 37


i
Fundamentals of C++ Programming
CONTENTS ii

4.1 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.2 Mixed Type Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.3 Operator Precedence and Associativity . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.4 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.5 Formatting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.6 Errors and Warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.6.1 Compile-time Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.6.2 Run-time Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.6.3 Logic Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.6.4 Compiler Warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.7 Arithmetic Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.8 Integers versus Floating-point Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.8.1 Integer Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.8.2 Floating-point Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.9 More Arithmetic Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.10 Bitwise Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
4.11 Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.12 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

5 Conditional Execution 85
5.1 Type bool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
5.2 Boolean Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
5.3 The Simple if Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.4 Compound Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
5.5 The if/else Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
5.6 Compound Boolean Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.7 Nested Conditionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
5.8 Multi-way if/else Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
5.9 Errors in Conditional Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
5.10 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

6 Iteration 123
6.1 The while Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
6.2 Nested Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
6.3 Abnormal Loop Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
ii
Fundamentals of C++ Programming
CONTENTS iii

6.3.1 The break statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140


6.3.2 The goto Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
6.3.3 The continue Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
6.4 Infinite Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
6.5 Iteration Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
6.5.1 Drawing a Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
6.5.2 Printing Prime Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
6.6 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

7 Other Conditional and Iterative Statements 159


7.1 The switch Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
7.2 The Conditional Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
7.3 The do/while Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
7.4 The for Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
7.5 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173

8 Using Functions 179


8.1 Introduction to Using Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
8.2 Standard Math Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
8.3 Maximum and Minimum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
8.4 clock Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
8.5 Character Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
8.6 Random Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
8.7 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197

9 Writing Functions 201


9.1 Function Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
9.2 Using Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
9.3 Pass by Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
9.4 Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
9.4.1 Better Organized Prime Generator . . . . . . . . . . . . . . . . . . . . . . . . . . 217
9.4.2 Command Interpreter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
9.4.3 Restricted Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
9.4.4 Better Die Rolling Simulator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
9.4.5 Tree Drawing Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
iii
Fundamentals of C++ Programming
CONTENTS iv

9.4.6 Floating-point Equality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224


9.4.7 Multiplication Table with Functions . . . . . . . . . . . . . . . . . . . . . . . . . 227
9.5 Organizing Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
9.6 Commenting Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
9.7 Custom Functions versus Standard Functions . . . . . . . . . . . . . . . . . . . . . . . . 234
9.8 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236

10 Managing Functions and Data 241


10.1 Global Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
10.2 Static Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
10.3 Overloaded Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
10.4 Default Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
10.5 Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
10.6 Making Functions Reusable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
10.7 Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
10.8 Reference Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
10.9 Pass by Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
10.9.1 Pass by Reference via Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
10.9.2 Pass by Reference via References . . . . . . . . . . . . . . . . . . . . . . . . . . 276
10.10Higher-order Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
10.11Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280

11 Sequences 289
11.1 Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
11.1.1 Declaring and Using Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
11.1.2 Traversing a Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
11.1.3 Vector Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
11.1.4 Vectors and Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
11.1.5 Multidimensional Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
11.2 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
11.2.1 Static Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
11.2.2 Pointers and Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
11.2.3 Dynamic Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
11.2.4 Copying an Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
11.2.5 Multidimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
iv
Fundamentals of C++ Programming
CONTENTS v

11.2.6 C Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334


11.2.7 Command-line Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
11.3 Vectors versus Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
11.4 Prime Generation with a Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
11.5 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345

12 Sorting and Searching 353


12.1 Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
12.2 Flexible Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
12.3 Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
12.3.1 Linear Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
12.3.2 Binary Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
12.4 Vector Permutations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
12.5 Randomly Permuting a Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
12.6 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383

13 Standard C++ Classes 385


13.1 String Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
13.2 Input/Output Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
13.3 File Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
13.4 Complex Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
13.5 Better Pseudorandom Number Generation . . . . . . . . . . . . . . . . . . . . . . . . . . 400
13.6 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407

14 Custom Objects 409


14.1 Object Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
14.2 Instance Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
14.3 Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
14.4 Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
14.5 Defining a New Numeric Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
14.6 Encapsulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
14.7 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432

15 Fine Tuning Objects 439


15.1 Passing Object Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
15.2 Pointers to Objects and Object Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
v
Fundamentals of C++ Programming
CONTENTS vi

15.3 The this Pointer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444


15.4 const Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
15.5 Separating Method Declarations and Definitions . . . . . . . . . . . . . . . . . . . . . . . 448
15.6 Preventing Multiple Inclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
15.7 Overloaded Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458
15.7.1 Operator Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
15.7.2 Operator Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
15.8 static Members . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
15.9 Classes versus structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466
15.10Friends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
15.11Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472

16 Building some Useful Classes 479


16.1 A Better Rational Number Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
16.2 Stopwatch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
16.3 Sorting with Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
16.4 Automating Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
16.5 Convenient Higher-quality Pseudorandom Numbers . . . . . . . . . . . . . . . . . . . . . 495
16.6 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498

17 Inheritance and Polymorphism 501


17.1 I/O Stream Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
17.2 Inheritance Mechanics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
17.3 Uses of Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
17.4 Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515
17.5 Alternative to Inheritance and Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . 520
17.6 Adapter Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527
17.7 Protected Members . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
17.8 Fine Tuning Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
17.9 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548

18 Memory Management 551


18.1 Memory Available to C++ Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 551
18.2 Manual Memory Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
18.3 Linked Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
vi
Fundamentals of C++ Programming
CONTENTS vii

18.4 Resource Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566


18.5 Rvalue References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586
18.6 Smart Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598

19 Generic Programming 615


19.1 Function Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
19.2 Class Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626
19.3 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640

20 The Standard Template Library 641


20.1 Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641
20.2 Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648
20.3 Iterator Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653
20.4 Lambda Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
20.5 Algorithms in the Standard Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
20.5.1 Processing the elements in a container . . . . . . . . . . . . . . . . . . . . . . . . 670
20.5.2 Creating a sequence of increasing integers . . . . . . . . . . . . . . . . . . . . . . 674
20.5.3 Locating an element in a container . . . . . . . . . . . . . . . . . . . . . . . . . . 675
20.5.4 Sorting elements of a container . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
20.5.5 Copying elements of a container . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
20.5.6 Transforming elements of a container . . . . . . . . . . . . . . . . . . . . . . . . 685
20.5.7 Counting elements of a container . . . . . . . . . . . . . . . . . . . . . . . . . . 686
20.5.8 Counting select elements of a container . . . . . . . . . . . . . . . . . . . . . . . 687
20.5.9 Copying select elements of a container . . . . . . . . . . . . . . . . . . . . . . . 687
20.5.10 Finding select elements of a container . . . . . . . . . . . . . . . . . . . . . . . . 688
20.5.11 Finding non-qualifying elements of a container . . . . . . . . . . . . . . . . . . . 689
20.5.12 Permuting elements of a container . . . . . . . . . . . . . . . . . . . . . . . . . . 690
20.5.13 Populating elements of a container . . . . . . . . . . . . . . . . . . . . . . . . . . 691
20.5.14 Reducing elements of a container . . . . . . . . . . . . . . . . . . . . . . . . . . 692
20.5.15 Partitioning a container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695
20.5.16 Combining the elements of two containers . . . . . . . . . . . . . . . . . . . . . . 697
20.5.17 Removing elements from a container . . . . . . . . . . . . . . . . . . . . . . . . 701
20.5.18 Removing select elements from a container . . . . . . . . . . . . . . . . . . . . . 704
20.5.19 Reversing the elements in a container . . . . . . . . . . . . . . . . . . . . . . . . 705
20.5.20 Other algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
vii
Fundamentals of C++ Programming
CONTENTS viii

20.6 Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707

21 Associative Containers 715


21.1 Associative Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
21.2 The std::set Data Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
21.3 Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
21.4 The std::map Data Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725
21.5 The std::unordered_map Data Type . . . . . . . . . . . . . . . . . . . . . . . . . . 730
21.6 Counting with Associative Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 732
21.7 Grouping with Associative Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 737
21.8 Memoization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740

22 Handling Exceptions 747


22.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
22.2 Exception Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748
22.3 Custom Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
22.4 Catching Multiple Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758
22.5 Exception Mechanics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761
22.6 Using Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764

Appendices 769

A Using Visual Studio 2015 to Develop C++ Programs 769

B Command Line Development 775


B.0.1 Visual Studio Command Line Tools . . . . . . . . . . . . . . . . . . . . . . . . . 776
B.0.2 Developing C++ Programs with the GNU Tools . . . . . . . . . . . . . . . . . . . 778

Bibliography 781

Index 782

viii
Fundamentals of C++ Programming
ix

Preface

Legal Notices and Information


Permission is hereby granted to make hardcopies and freely distribute the material herein under the
following conditions:

• The copyright and this legal notice must appear in any copies of this document made in whole or in
part.
• None of material herein can be sold or otherwise distributed for commercial purposes without written
permission of the copyright holder.

• Instructors at any educational institution may freely use this document in their classes as a primary
or optional textbook under the conditions specified above.

A local electronic copy of this document may be made under the terms specified for hard copies:

• The copyright and these terms of use must appear in any electronic representation of this document
made in whole or in part.
• None of material herein can be sold or otherwise distributed in an electronic form for commercial
purposes without written permission of the copyright holder.

• Instructors at any educational institution may freely store this document in electronic form on a local
server as a primary or optional textbook under the conditions specified above.

Additionally, a hardcopy or a local electronic copy must contain the uniform resource locator (URL)
providing a link to the original content so the reader can check for updated and corrected content. The
current standard URL is http://python.cs.southern.edu/cppbook/progcpp.pdf.

If you are an instructor using this book in one or more of your courses, please let me know. Keeping track of how
and where this book is used helps me justify to my employer that it is providing a useful service to the community and
worthy of the time I spend working on it. Simply send a message to [email protected] with your name,
your institution, and the course(s) in which you use it.
The source code for all labeled listings is available at
https://github.com/halterman/CppBook-SourceCode.

ix
Fundamentals of C++ Programming
1

Chapter 1

The Context of Software Development

A computer program, from one perspective, is a sequence of instructions that dictate the flow of electrical
impulses within a computer system. These impulses affect the computer’s memory and interact with the
display screen, keyboard, mouse, and perhaps even other computers across a network in such a way as
to produce the “magic” that permits humans to perform useful tasks, solve high-level problems, and play
games. One program allows a computer to assume the role of a financial calculator, while another transforms
the machine into a worthy chess opponent. Note the two extremes here:

• at the lower, more concrete level electrical impulses alter the internal state of the computer, while
• at the higher, more abstract level computer users accomplish real-world work or derive actual plea-
sure.

So well is the higher-level illusion achieved that most computer users are oblivious to the lower-level
activity (the machinery under the hood, so to speak). Surprisingly, perhaps, most programmers today write
software at this higher, more abstract level also. An accomplished computer programmer can develop so-
phisticated software with little or no interest or knowledge of the actual computer system upon which it
runs. Powerful software construction tools hide the lower-level details from programmers, allowing them
to solve problems in higher-level terms.
The concepts of computer programming are logical and mathematical in nature. In theory, computer
programs can be developed without the use of a computer. Programmers can discuss the viability of a pro-
gram and reason about its correctness and efficiency by examining abstract symbols that correspond to the
features of real-world programming languages but appear in no real-world programming language. While
such exercises can be very valuable, in practice computer programmers are not isolated from their machines.
Software is written to be used on real computer systems. Computing professionals known as software engi-
neers develop software to drive particular systems. These systems are defined by their underlying hardware
and operating system. Developers use concrete tools like compilers, debuggers, and profilers. This chapter
examines the context of software development, including computer systems and tools.

1.1 Software

A computer program is an example of computer software. Software makes a computer a truly universal
machine transforming it into the proper tool for the task at hand. One can refer to a program as a piece of
1
Fundamentals of C++ Programming
1.2. DEVELOPMENT TOOLS 2

software as if it were a tangible object, but software is actually quite intangible. It is stored on a medium. A
hard drive, a CD, a DVD, and a USB pen drive are all examples of media upon which software can reside.
The CD is not the software; the software is a pattern on the CD. In order to be used, software must be stored
in the computer’s memory. Typically computer programs are loaded into memory from a medium like the
computer’s hard disk. An electromagnetic pattern representing the program is stored on the computer’s hard
drive. This pattern of electronic symbols must be transferred to the computer’s memory before the program
can be executed. The program may have been installed on the hard disk from a CD or from the Internet. In
any case, the essence that was transferred from medium to medium was a pattern of electronic symbols that
direct the work of the computer system.
These patterns of electronic symbols are best represented as a sequence of zeroes and ones, digits from
the binary (base 2) number system. An example of a binary program sequence is

10001011011000010001000001001110

To the underlying computer hardware, specifically the processor, a zero here and three ones there might
mean that certain electrical signals should be sent to the graphics device so that it makes a certain part of
the display screen red. Unfortunately, only a minuscule number of people in the world would be able to
produce, by hand, the complete sequence of zeroes and ones that represent the program Microsoft Word for
an Intel-based computer running the Windows 8 operating system. Further, almost none of those who could
produce the binary sequence would claim to enjoy the task.
The Word program for older Mac OS X computers using a PowerPC processor works similarly to the
Windows version and indeed is produced by the same company, but the program is expressed in a com-
pletely different sequence of zeroes and ones! The Intel Core i7 processor in the Windows machine accepts
a completely different binary language than the PowerPC processor in the Mac. We say the processors have
their own machine language.

1.2 Development Tools

If very few humans can (or want) to speak the machine language of the computers’ processors and software
is expressed in this language, how has so much software been developed over the years?
Software can be represented by printed words and symbols that are easier for humans to manage than
binary sequences. Tools exist that automatically convert a higher-level description of what is to be done
into the required lower-level code. Higher-level programming languages like C++ allow programmers to
express solutions to programming problems in terms that are much closer to a natural language like English.
Some examples of the more popular of the hundreds of higher-level programming languages that have been
devised over the past 60 years include FORTRAN, COBOL, Lisp, Haskell, C, Perl, Python, Java, and C#.
Most programmers today, especially those concerned with high-level applications, usually do not worry
about the details of underlying hardware platform and its machine language.
One might think that ideally such a conversion tool would accept a description in a natural language,
such as English, and produce the desired executable code. This is not possible today because natural lan-
guages are quite complex compared to computer programming languages. Programs called compilers that
translate one computer language into another have been around for over 60 years, but natural language
processing is still an active area of artificial intelligence research. Natural languages, as they are used by
most humans, are inherently ambiguous. To understand properly all but a very limited subset of a natu-
ral language, a human (or artificially intelligent computer system) requires a vast amount of background
knowledge that is beyond the capabilities of today’s software. Fortunately, programming languages provide
2
Fundamentals of C++ Programming
1.2. DEVELOPMENT TOOLS 3

a relatively simple structure with very strict rules for forming statements that can express a solution to any
problem that can be solved by a computer.
Consider the following program fragment written in the C++ programming language:
subtotal = 25;
tax = 3;
total = subtotal + tax;

These three lines do not make up a complete C++ program; they are merely a piece of a program. The state-
ments in this program fragment look similar to expressions in algebra. We see no sequence of binary digits.
Three words, subtotal, tax, and total, called variables, are used to hold information. Mathemati-
cians have used variables for hundreds of years before the first digital computer was built. In programming,
a variable represents a value stored in the computer’s memory. Familiar operators (= and +) are used in-
stead of some cryptic binary digit sequence that instructs the processor to perform the operation. Since this
program is expressed in the C++ language, not machine language, it cannot be executed directly on any
processor. A C++ compiler is used to translate the C++ code into machine code.
The higher-level language code is called source code. The compiled machine language code is called
the target code. The compiler translates the source code into the target machine language.
The beauty of higher-level languages is this: the same C++ source code can be compiled to different
target platforms. The target platform must have a C++ compiler available. Minor changes in the source code
may be required because of architectural differences in the platforms, but the work to move the program
from one platform to another is far less than would be necessary if the program for the new platform had
to be rewritten by hand in the new machine language. Just as importantly, when writing the program the
human programmer is free to think about writing the solution to the problem in C++, not in a specific
machine language.
Programmers have a variety of tools available to enhance the software development process. Some
common tools include:

• Editors. An editor allows the user to enter the program source code and save it to files. Most pro-
gramming editors increase programmer productivity by using colors to highlight language features.
The syntax of a language refers to the way pieces of the language are arranged to make well-formed
sentences. To illustrate, the sentence
The tall boy runs quickly to the door.
uses proper English syntax. By comparison, the sentence
Boy the tall runs door to quickly the.
is not correct syntactically. It uses the same words as the original sentence, but their arrangement
does not follow the rules of English.
Similarly, programmers must follow strict syntax rules to create well-formed computer programs.
Only well-formed programs are acceptable and can be compiled and executed. Some syntax-aware
editors can use colors or other special annotations to alert programmers of syntax errors before the
program is compiled.
• Compilers. A compiler translates the source code to target code. The target code may be the machine
language for a particular platform or embedded device. The target code could be another source lan-
guage; for example, the earliest C++ compiler translated C++ into C, another higher-level language.
The resulting C code was then processed by a C compiler to produce an executable program. C++
compilers today translate C++ directly into machine language.
3
Fundamentals of C++ Programming
1.2. DEVELOPMENT TOOLS 4

Figure 1.1 Source code to target code sequence

Concept of
(Design
problem
program logic)
solution

Editor (Edit) Programmer’s


responsibility

#include <io

Source code
using namespace
std;
int main()
{
srand(23);
int n;
n = rand();

Library proc(n);

declarations
(source code)

Preprocessor
istream cin;
ostream cout;
int rand();
void sand();
(Preprocess)
typedef unsigned U
#define NULL (0)

#include <io

Enhanced using namespace


std;
int main()
{

source code srand(23);


int n;
n = rand();
proc(n);

Automated
Compiler (Compile)
by tools

Object code 101100010101


000010001100
1100001111010100
0011101101110011
1000000010000110
0111000000111111
1100111011001001

Pre-compiled 0000100001111000
0001110111101101
1101111011111010

libraries
(object code)
11000011110
00111011011
1000000010000110
0111000000111111
1100111011001001
Linker (Link)
0000100001111000
0001110111101101
1101111011111010

101100010101
000010001100

Executable 1100001111010100
0011101101110011
1000000010000110
0111000000111111

program 1100111011001001
0000100001111000
0001110111101101
1101111011111010

4
Fundamentals of C++ Programming
1.2. DEVELOPMENT TOOLS 5

The complete set of build tools for C++ includes a preprocessor, compiler, and linker:

– Preprocessor—adds to or modifies the contents of the source file before the compiler begins
processing the code. We use the services of the preprocessor mainly to #include information
about library routines our programs use.
– Compiler—translates C++ source code to machine code.
– Linker—combines the compiler-generated machine code with precompiled library code or
compiled code from other sources to make a complete executable program. Most compiled
C++ code is incapable of running by itself and needs some additional machine code to make
a complete executable program. The missing machine code has been precompiled and stored
in a repository of code called a library. A program called a linker combines the programmer’s
compiled code and the library code to make a complete program.

We generally do not think about the preprocessor, compiler, and linker working as three separate
programs (although they do); the tools we use make it appear as only one process is taking place:
translating our source code to an executable program.

• Debuggers. A debugger allows a programmer to more easily trace a program’s execution in order to
locate and correct errors in the program’s implementation. With a debugger, a developer can simulta-
neously run a program and see which line in the source code is responsible for the program’s current
actions. The programmer can watch the values of variables and other program elements to see if their
values change as expected. Debuggers are valuable for locating errors (also called bugs) and repairing
programs that contain errors. (See Section 4.6 for more information about programming errors.)

• Profilers. A profiler collects statistics about a program’s execution allowing developers to tune ap-
propriate parts of the program to improve its overall performance. A profiler indicates how many
times a portion of a program is executed during a particular run, and how long that portion takes to
execute. Profilers also can be used for testing purposes to ensure all the code in a program is actually
being used somewhere during testing. This is known as coverage. It is common for software to fail
after its release because users exercise some part of the program that was not executed anytime during
testing. The main purpose of profiling is to find the parts of a program that can be improved to make
the program run faster.

• Build systems. A build system is a tool that allows programmers to manage more easily all the
files and resources required for producing a complex piece of software. Build systems automate the
coordination of the compiler and linker and enable developers to specify the necessary components
in a platform-independent manner. As an example, the command to compile a C++ file on an Apple
computer using the Clang C++ compiler is different from the command for invoking the Visual C++
compiler on a computer running Microsoft Windows. A good build system allows programmers to
describe the build process in a generic way. The tool then uses this generic description to produce the
platform-specific instructions required for specific operating systems and hardware combinations.

The programming components of the development process are illustrated in Figure 1.1.
Many developers use integrated development environments (IDEs). An IDE includes editors, debuggers,
a build system, and other programming aids in one comprehensive program. Examples of IDEs for C++
include Microsoft’s Visual Studio 2022, the Eclipse Foundation’s Eclipse CDT, and Apple’s XCode.
Despite the plethora of tools (and tool vendors’ claims), the programming process for all but trivial
programs is not automatic. Good tools are valuable and certainly increase the productivity of developers,
but they cannot write software. There are no substitutes for sound logical thinking, creativity, common
sense, and, of course, programming experience.
5
Fundamentals of C++ Programming
1.3. LEARNING PROGRAMMING WITH C++ 6

1.3 Learning Programming with C++

Bjarne Stroustrup of AT&T Bell Labs created C++ in the mid 1980s. C++ is an extension of the program-
ming language C, a product of AT&T Bell Labs from the early 1970s. C was developed to write the Unix
operating system, and C is widely used for systems-level software and embedded systems development.
C++ initially provided object-oriented programming features (see Chapter 13 and Chapter 14) and later
added generic programming capabilities. C++’s close relationship to C allows C++ programs to utilize a
large collection of code developed in C.
C++ is widely used in industry for commercial software development. It is an industrial strength pro-
gramming language used for developing complex systems in business, science, and engineering. Examples
of software written in C++ include Microsoft Windows 8, Microsoft Office, macOS, and Adobe Creative
Suite.
In order to meet the needs of commercial software development and accomplish all that it does, C++
itself is complex. While experienced programmers can accomplish great things with C++, beginners some-
times have a difficult time with it. Professional software developers enjoy the flexible design options that
C++ permits, but beginners need more structure and fewer options so they can master simpler concepts
before moving on to more complex ones.
This book does not attempt to cover all the facets of the C++ programming language. Experienced
programmers should look elsewhere for books that cover C++ in much more detail. The focus here is on
introducing programming techniques and developing good habits. To that end, our approach avoids some
of the more esoteric features of C++ and concentrates on the programming basics that transfer directly
to other imperative programming languages such as Java, C#, and Python. We stick with the basics and
explore more advanced features of C++ only when necessary to handle the problem at hand.

1.4 Exercises

1. What is a compiler?
2. How is compiled code different from source code?
3. What tool does a programmer use to produce C++ source code?

4. What tool(s) does a programmer use to convert C++ source code into executable machine code?
5. What does the linker do?
6. Does the linker deal with files containing source code or machine language code?

7. What does the preprocessor do to source code?


8. List several advantages developing software in a higher-level language has over developing software
in machine language.
9. How can an IDE improve a programmer’s productivity?

10. Name a popular C++ IDE is used by programmers developing for Microsoft Windows.
11. Name a popular C++ IDE is used by programmers developing for Apple macOS.

6
Fundamentals of C++ Programming
7

Chapter 2

Writing a C++ Program

Properly written C++ programs have a particular structure. The syntax must be correct, or the compiler
will generate error messages and not produce executable machine language. This chapter introduces C++
by providing some simple example programs and associated fundamental concepts. Most of the concepts
presented in this chapter are valid in many other programming languages as well. While other languages
may implement the concepts using slightly different syntax, the ideas are directly transferable to other
languages like C, Java, C#, and Ada.

2.1 General Structure of a Simple C++ Program

Listing 2.1 (simple.cpp) is one of the simplest C++ programs that does something:

Listing 2.1: simple.cpp


#include <iostream>

int main() {
std::cout << "This is a simple C++ program!\n";
}

You can type the text as shown in Listing 2.1 (simple.cpp) into an editor and save it to a file named
simple.cpp. The actual name of the file is irrelevant, but the name “simple” accurately describes the nature
of this program. The extension .cpp is a common extension used for C++ source code.
After creating this file with a text editor and compiling it, you can run the program. The program prints
the message
This is a simple C++ program!

Listing 2.1 (simple.cpp) contains four non-blank lines of code:

• #include <iostream>
This line is a preprocessing directive. All preprocessing directives within C++ source code begin
with a # symbol. This one directs the preprocessor to add some predefined source code to our exist-
7
Fundamentals of C++ Programming
2.2. EDITING, COMPILING, AND RUNNING THE PROGRAM 8

ing source code before the compiler begins to process it. This process is done automatically and is
invisible to us.
Here we want to use an object from the iostream library, a collection precompiled C++ code that
C++ programs (like ours) can use. The iostream library contains elements that handle input and
output (I/O)—printing to the display, getting user input from the keyboard, and dealing with files.
One of the items used in Listing 2.1 (simple.cpp), std::cout, is not part of the C++ language itself.
This item, along with other things related to input and output, were developed in C++, compiled, and
stored in the iostream library. The compiler needs to be aware of these iostream items so it
can compile our program. The #include directive specifies a file, called a header, that contains
the specifications for the library code. The compiler checks how we use std::cout within our
code against its specification in the <iostream> header to ensure that we are using the library code
correctly.
Most of the programs we write use this #include <iostream> directive, and some programs
we will write in the future will #include other headers as well.

• int main() {
This specifies the real beginning of our program. Here we are declaring a function named main.
All C++ programs must contain this function to be executable. Details about the meaning of int
and the parentheses will appear in later chapters. More general information about functions appear in
Chapter 8 and Chapter 9.
The opening curly brace at the end of the line marks the beginning of the body of a function. The
body of a function contains the statements the function is to execute.

• std::cout << "This is a simple C++ program!\n";


The body of our main function contains only one statement. This statement directs the executing
program to print the message This is a simple C++ program! on the screen. A statement is the
fundamental unit of execution in a C++ program. Functions contain statements that the compiler
translates into executable machine language instructions. C++ has a variety of different kinds of
statements, and the chapters that follow explore these various kinds of statements. All statements in
C++ end with a semicolon (;). A more detailed explanation of this statement appears below.

• }
The closing curly brace marks the end of the body of a function. Both the open curly brace and close
curly brace are required for every function definition.

Note which lines in the program end with a semicolon (;) and which do not. Do
not put a semicolon after the #include preprocessor directive. Do not put a
semicolon on the line containing main, and do not put semicolons after the curly
braces.

2.2 Editing, Compiling, and Running the Program

C++ programmers have two options for C++ development environments. One option involves a command-
line environment with a collection of independent tools. The other option is to use an IDE (see Section 1.2)
8
Fundamentals of C++ Programming
2.3. VARIATIONS OF OUR SIMPLE PROGRAM 9

which combines all the tools into a convenient package. Visual Studio is the dominant IDE on the Microsoft
Windows platform, and Apple Mac developers often use the XCode IDE.
The myriad of features and configuration options in these powerful IDEs can be bewildering to those
learning how to program. In a command-line environment the programmer needs only type a few simple
commands into a console window to edit, compile, and execute programs. Some developers prefer the
simplicity and flexibility of command-line build environments, especially for less complex projects.
One prominent command-line build system is the GNU Compiler Collection (http://gcc.gnu.
org), or GCC for short. The GCC C++ compiler, called g++, is one of most C++ standards conforming
compilers available. The GCC C++ compiler toolset is available for the Microsoft Windows, Apple Mac,
and Linux platforms, and it is a free, open-source software project with a world-wide development team.

2.3 Variations of our simple program

Listing 2.2 (simple2.cpp) shows an alternative way of writing Listing 2.1 (simple.cpp).

Listing 2.2: simple2.cpp


#include <iostream>

using std::cout;

int main() {
cout << "This is a simple C++ program!\n";
}

The using directive in Listing 2.2 (simple2.cpp) allows us to use a shorter name for the std::cout
printing object. We can omit the std:: prefix and use the shorter name, cout. This directive is optional,
but if we omit it, we must use the longer name. The name std stands for “standard,” and the std prefix
indicates that cout is part of a collection of names called the standard namespace. The std namespace
holds names for all the standard C++ types and functions that must be available to all standards-conforming
C++ development environments. Components outside the standard library provided by third-party develop-
ers reside in their own separately-named namespaces. These include open-source projects and commercial
libraries.
Listing 2.3 (simple3.cpp) shows another way to use the shorter name for cout within a C++ program.

Listing 2.3: simple3.cpp


#include <iostream>

using namespace std;

int main() {
cout << "This is a simple C++ program!\n";
}

While Listing 2.2 (simple2.cpp) made the name cout known to the compiler via its focused using di-
rective, Listing 2.3 (simple3.cpp) provides a blanket using directive that makes all names in the std
namespace available to the compiler. This approach offers some advantages for smaller programs, such as
examples in books and online tutorials. This blanket using directive allows programmers to use shorter
9
Fundamentals of C++ Programming
2.3. VARIATIONS OF OUR SIMPLE PROGRAM 10

names as in the more more focused using directives, and it also can use fewer lines of code than the more
focused using directives, especially when the program uses multiple elements from the std namespace.
Our choice of using directives (or not) makes no difference in our final product, the executable pro-
gram. The compiler generates the same machine language code for all three versions—no using, focused
using, and blanket using. We thus must select an approach that enhances our ability to write and manage
our software projects.
It is important to note that while this blanket using approach has its place, its use generally is discour-
aged for more complex software projects. At this point we cannot fully appreciate the rationale for avoiding
the using namespace std directive, but later, in Section 20.6, we will have enough experience to
understand the disadvantages of the blanket using namespace std directive. We will strive for best
practices from the start and avoid the blanket using statement. We generally will use the full names of
the elements in the std namespace and use the more focused using directives in our code when it makes
sense to do so.
The statement in the main function in any of the three versions of our program uses the services of an
object called std::cout. The std::cout object prints text on the computer’s screen. The text of the
message as it appears in the C++ source code is called a string, for string of characters. Strings are enclosed
within quotation marks ("). The symbols << make up the insertion operator. You can think of the message
to be printed as being “inserted” into the cout object. The cout object represents the output stream;
that is, text that the program prints to the console window. The end of the message contains the symbol
sequence \n. This known as a character escape sequence, and this combination of backslash and the letter
n represents the newline character. It indicates that the printing on that line is complete, and any subsequent
printing should occur on the next line. This newline character effectively causes the cursor to move down
to the next line. If you read the statement from left to right, the cout object, which is responsible for
displaying text on the screen, receives the text to print terminated with the newline character to move to the
next line.
For simplicity, we’ll refer to this type of statement as a print statement, even though the word print does
not appear anywhere in the statement.
With minor exceptions, any statement in C++ must appear within a function definition. Our single print
statement appears within the function named main.
Any function, including main, may contain multiple statements. In Listing 2.4 (arrow.cpp), six print
statements draw an arrow on the screen:

Listing 2.4: arrow.cpp


#include <iostream>

int main() {
std::cout << " * \n";
std::cout << " *** \n";
std::cout << " ***** \n";
std::cout << " * \n";
std::cout << " * \n";
std::cout << " * \n";
}

The output of Listing 2.4 (arrow.cpp) is


*
***
*****
10
Fundamentals of C++ Programming
2.3. VARIATIONS OF OUR SIMPLE PROGRAM 11

*
*
*

Each print statement “draws” a horizontal slice of the arrow. The six statements
std::cout << " * \n";
std::cout << " *** \n";
std::cout << " ***** \n";
std::cout << " * \n";
std::cout << " * \n";
std::cout << " * \n";

constitute the body of the main function. The body consists of all the statements between the open curly
brace ({) and close curly brace (}). We say that the curly braces delimit the body of the function. The word
delimit means to determine the boundaries or limits of something. The { symbol determines the beginning
of the function’s body, and the } symbol specifies the end of the function’s body.
We can rewrite Listing 2.4 (arrow.cpp) to achieve the same effect with only one long print statement as
Listing 2.5 (arrow2.cpp) shows.

Listing 2.5: arrow2.cpp

#include <iostream>

int main() {
std::cout << " * \n"
<< " *** \n"
<< " ***** \n"
<< " * \n"
<< " * \n"
<< " * \n";
}

At first, Listing 2.4 (arrow.cpp) and Listing 2.5 (arrow2.cpp) may appear to be identical, but upon closer
inspection of this new program we see that std::cout appears only once within main, and only one
semicolon (;) appears within main. Since semicolons in C++ terminate statements, there really is only one
statement. Notice that a single statement can be spread out over several lines. The statement within main
appearing as
std::cout << " * \n"
<< " *** \n"
<< " ***** \n"
<< " * \n"
<< " * \n"
<< " * \n";

could have just as easily been written as


std::cout << " * \n" << " *** \n"
<< " ***** \n" << " * \n"
<< " * \n" << " * \n";
11
Fundamentals of C++ Programming
2.4. TEMPLATE FOR SIMPLE C++ PROGRAMS 12

but the first way of expressing it better portrays how the output will appear. Read this second version
carefully to convince yourself that the printed pieces will indeed flow to the std::cout printing object
in the proper sequence to produce the same picture of the arrow.

Consider the mistake of putting semicolons at the end of each of the lines in the
“one statement” version:
std::cout << " * \n";
<< " *** \n";
<< " ***** \n";
<< " * \n";
<< " * \n";
<< " * \n";
If we put this code fragment in main, the program will not compile. The reason
is simple—the semicolon at the end of the first line terminates the statement on
that line. The compiler expects a new statement on the next line, but
<< " *** \n";
is not a complete legal C++ statement since the << operator is missing the
std::cout object. The string " *** \n" has nothing to “flow into.”

Listing 2.6 (empty.cpp) is even simpler than Listing 2.1 (simple.cpp).

Listing 2.6: empty.cpp

int main() {
}

Since Listing 2.6 (empty.cpp) does not use the std::cout object and so does not need the #include
and using directives. While it is legal and sometimes even useful in C++ to write functions with empty
bodies, such functions will do nothing when they execute. Listing 2.6 (empty.cpp) with its empty main
function is, therefore, truly the simplest executable C++ program we can write, but it does nothing when
we run it!
In general, a C++ program may contain multiple functions, but we defer such generality until Chapter 9.
For now, we will restrict our attention to programs with only a main function.

2.4 Template for simple C++ programs

For our immediate purposes all the programs we write will have the form shown in Figure 2.1.
Our programs generally will print something, so we need the #include directive that brings the
std::cout definition from <iostream> into our program. Depending on what we need our program
to do, we may need additional #include directives. The main function definition is required for an
executable program, and we will fill its body with statements that make our program do as we wish. Later,
our programs will become more sophisticated, and we will need to augment this simple template.
12
Fundamentals of C++ Programming
2.5. EXERCISES 13

Figure 2.1 The general structure of a very simple C++ program.

include directives

int main() {

program statements

2.5 Exercises

1. What preprocessor directive is necessary to use statements with the std::cout printing stream
object?
2. What statement allows the short name cout to be used instead of std::cout?
3. What does the name std stand for?

4. All C++ programs must have a function named what?


5. The body of main is enclosed within what symbols?
6. What operator directs information to the std::cout output stream?
7. Write a C++ program that prints your name in the console window.

8. Write a C++ program that prints your first and last name in the console window. Your first name
should appear on one line, and your last name appear on the next line.
9. What other files must you distribute with your executable file so that your program will run on a
Windows PC without Visual Studio installed?

10. Can a single statement in C++ span multiple lines in the source code?

13
Fundamentals of C++ Programming
2.5. EXERCISES 14

14
Fundamentals of C++ Programming
15

Chapter 3

Values and Variables

In this chapter we explore some building blocks that are used to develop C++ programs. We experiment
with the following concepts:

• numeric values

• variables

• declarations

• assignment

• identifiers

• reserved words

In the next chapter we will revisit some of these concepts in the context of other data types.

3.1 Integer Values

The number four (4) is an example of a numeric value. In mathematics, 4 is an integer value. Integers are
whole numbers, which means they have no fractional parts, and an integer can be positive, negative, or zero.
Examples of integers include 4, −19, 0, and −1005. In contrast, 4.5 is not an integer, since it is not a whole
number.
C++ supports a number of numeric and non-numeric values. In particular, C++ programs can use integer
values. It is easy to write a C++ program that prints the number four, as Listing 3.1 (number4.cpp) shows.

Listing 3.1: number4.cpp


#include <iostream>

int main() {
std::cout << 4 << '\n';
}
15
Fundamentals of C++ Programming
3.1. INTEGER VALUES 16

Notice that unlike the programs we saw earlier, Listing 3.1 (number4.cpp) does not use quotation marks
("). The number 4 appears unadorned with no quotes. The expression '\n' represents a single newline
character. Multiple characters comprising a string appear in double quotes ("), but, in C++, a single char-
acter represents a distinct type of data and is enclosed within single quotes ('). (Section 3.8 provides more
information about C++ characters.) Compare Listing 3.1 (number4.cpp) to Listing 3.2 (number4-alt.cpp).

Listing 3.2: number4-alt.cpp


#include <iostream>

int main() {
std::cout << "4\n";
}

Both programs behave identically, but Listing 3.1 (number4.cpp) prints the value of the number four, while
Listing 3.2 (number4-alt.cpp) prints a message containing the digit four. The distinction here seems unim-
portant, but we will see in Section 3.2 that the presence or absence of the quotes can make a big difference
in the output.
The statement

std::cout << "4\n";

sends one thing to the output stream, the string "4\n". The statement

std::cout << 4 << '\n';

sends two things to the output stream, the integer value 4 and the newline character '\n'.

16
Fundamentals of C++ Programming
3.1. INTEGER VALUES 17

In published C++ code you sometimes will see a statement such as the following:
std::cout << 4 << std::endl;
This statement on the surface behaves exactly like the following statement:
std::cout << 4 << '\n';
but the two expressions std::endl and '\n' do not mean exactly the same
thing. The std::endl expression does involve a newline character, but it also
performs some additional work that normally is not necessary.
Programs that do significant printing may execute faster if they terminate their
output lines with '\n' instead of std::endl. The difference in speed is neg-
ligible when printing to the console, but the different can be great when printing
to files or other output streams. For most of the programs we consider, the differ-
ence in program execution speed between the two is imperceptible; nonetheless,
we will prefer '\n' for printing newlines because it is a good habit to form (and
it requires five fewer keystrokes when editing code).
The three major modern computing platforms are Microsoft Windows, Apple ma-
cOS, and Linux. Windows handles newlines differently from macOS and Linux.
Historically, the character '\n' represents a new line, usually known as a line
feed or LF for short, and the character '\r' means carriage return, or CR for
short. The terminology comes from old-fashioned typewriters which feed a piece
of paper into a roller on a carriage that moves to the left as the user types (so the
imprinted symbols form left to right). At the end of a line, the user must advance
the roller so as to move the paper up by one line (LF) and move the carriage back
all the way to its left (CR). Windows uses the character sequence CR LF for new-
lines, while macOS and Linux use LF. This can be an issue when attempting to
edit text files written with an editor on one platform with an editor on a different
platform.
The good news is that the C++ standard guarantees that the std::cout output
stream translates the '\n' character as it appears in C++ source code into the
correct character sequence for the target platform. This means you can print '\n'
via std::cout, and it will behave identically on all the major platforms.

In C++ source code, integers may not contain commas. This means we must write the number two
thousand, four hundred sixty-eight as 2468, not 2,468. Modern C++ does support single quotes (') as
digit separators, as in 2'468. Using digit separators can improve the human comprehension reading large
numbers in C++ source code.
In mathematics, integers are unbounded; said another way, the set of mathematical integers is infinite.
In C++ the range of integers is limited because all computers have a finite amount of memory. The exact
range of integers supported depends on the computer system and particular C++ compiler. C++ on most
32-bit computer systems can represent integers in the range −2,147,483,648 to +2,147,483,647.
What happens if you exceed the range of C++ integers? Try Listing 3.3 (exceed.cpp) on your system.

Listing 3.3: exceed.cpp


#include <iostream>

int main() {
std::cout << -3000000000 << '\n';
17
Fundamentals of C++ Programming
3.2. VARIABLES AND ASSIGNMENT 18

Negative three billion is too large for 32-bit integers, however, and the program’s output is obviously wrong:
1294967296

The number printed was not even negative! Most C++ compilers will issue a warning about this statement.
Section 4.6 explores errors versus warnings in more detail. If the compiler finds an error in the source, it will
not generate the executable code. A warning indicates a potential problem and does not stop the compiler
from producing an executable program. Here we see that the programmer should heed this warning because
the program’s execution produces meaningless output.
This limited range of values is common among programming languages since each number is stored in
a fixed amount of memory. Larger numbers require more storage in memory. In order to model the infinite
set of mathematical integers an infinite amount of memory would be needed! As we will see later, C++
supports an integer type with a greater range. Section 4.8.1 provides some details about the implementation
of C++ integers.

3.2 Variables and Assignment

In algebra, variables are used to represent numbers. The same is true in C++, except C++ variables also can
represent values other than numbers. Listing 3.4 (variable.cpp) uses a variable to store an integer value and
then prints the value of the variable.

Listing 3.4: variable.cpp


#include <iostream>

int main() {
int x;
x = 10;
std::cout << x << '\n';
}

The main function in Listing 3.4 (variable.cpp) contains three statements:

• int x;
This is a declaration statement. All variables in a C++ program must be declared. A declaration
specifies the type of a variable. The word int indicates that the variable is an integer. The name
of the integer variable is x. We say that variable x has type int. C++ supports types other than
integers, and some types require more or less space in the computer’s memory. The compiler uses
the declaration to reserve the proper amount of memory to store the variable’s value. The declaration
enables the compiler to verify the programmer is using the variable properly within the program; for
example, we will see that integers can be added together just like in mathematics. For some other
data types, however, addition is not possible and so is not allowed. The compiler can ensure that a
variable involved in an addition operation is compatible with addition. It can report an error if it is
not.
The compiler will issue an error if a programmer attempts to use an undeclared variable. The com-
piler cannot deduce the storage requirements and cannot verify the variable’s proper usage if it not
declared. Once declared, a particular variable cannot be redeclared in the same context. A variable
may not change its type during its lifetime.
18
Fundamentals of C++ Programming
3.2. VARIABLES AND ASSIGNMENT 19

• x = 10;
This is an assignment statement. An assignment statement associates a value with a variable. The key
to an assignment statement is the symbol = which is known as the assignment operator. Here the
value 10 is being assigned to the variable x. This means the value 10 will be stored in the memory
location the compiler has reserved for the variable named x. We need not be concerned about where
the variable is stored in memory; the compiler takes care of that detail.
After we declare a variable we may assign and reassign it as often as necessary.

• std::cout << x << '\n';


This statement prints the variable x’s current value.

Note that the lack of quotation marks here is very important. If x has the value 10,
the statement
std::cout << x << '\n';
prints 10, the value of the variable x, but the statement
std::cout << "x" << '\n';
prints x, the message containing the single letter x.

The meaning of the assignment operator (=) is different from equality in mathematics. In mathematics,
= asserts that the expression on its left is equal to the expression on its right. In C++, = makes the variable
on its left take on the value of the expression on its right. It is best to read x = 5 as “x is assigned the
value 5,” or “x gets the value 5.” This distinction is important since in mathematics equality is symmetric:
if x = 5, we know 5 = x. In C++, this symmetry does not exist; the statement
5 = x;

attempts to reassign the value of the literal integer value 5, but this cannot be done, because 5 is always 5
and cannot be changed. Such a statement will produce a compiler error:

error C2106: ’=’ : left operand must be l-value

Variables can be reassigned different values as needed, as Listing 3.5 (multipleassignment.cpp) shows.

Listing 3.5: multipleassignment.cpp


#include <iostream>

int main() {
int x;
x = 10;
std::cout << x << '\n';
x = 20;
std::cout << x << '\n';
x = 30;
std::cout << x << '\n';
}
19
Fundamentals of C++ Programming
3.2. VARIABLES AND ASSIGNMENT 20

Observe the each print statement in Listing 3.5 (multipleassignment.cpp) is identical, but when the program
runs the print statements produce different results.
A variable may be given a value at the time of its declaration; for example, Listing 3.6 (variable-init.cpp)
is a variation of Listing 3.4 (variable.cpp).

Listing 3.6: variable-init.cpp


#include <iostream>

int main() {
int x = 10;
std::cout << x << '\n';
}

Notice that in Listing 3.6 (variable-init.cpp) the declaration and assignment of the variable x is performed in
one statement instead of two. This combined declaration and immediate assignment is called initialization.
C++ supports another syntax for initializing variables as shown in Listing 3.7 (alt-variable-init.cpp).

Listing 3.7: alt-variable-init.cpp


#include <iostream>

int main() {
int x{10};
std::cout << x << '\n';
}

This alternate form is not commonly used for simple variables, but it necessary for initializing more com-
plicated kinds of variables called objects. We introduce objects in Chapter 13 and Chapter 14.
Multiple variables of the same type can be declared and, if desired, initialized in a single statement. The
following statements declare three variables in one declaration statement:
int x, y, z;

The following statement declares three integer variables and initializes two of them:
int x = 0, y, z = 5;

Here y’s value is undefined. The declarations may be split up into multiple declaration statements:
int x = 0;
int y;
int z = 5;

In the case of multiple declaration statements the type name (here int) must appear in each statement.
The compiler maps a variable to a location in the computer’s memory. We can visualize a variable and
its corresponding memory location as a box as shown in Figure 3.1.
We name the box with the variable’s name. Figure 3.2 shows how the following sequence of C++ code
affects memory.
int a, b;
a = 2;
20
Fundamentals of C++ Programming
3.3. IDENTIFIERS 21

Figure 3.1 Representing a variable and its memory location as a box

a
5

b = 5;
a = b;
b = 4;

Importantly, the statement


a = b;

does not mean a and b refer to the same box (memory location). After this statement a and b still refer
to separate boxes (memory locations). It simply means the value stored in b’s box (memory location) has
been copied to a’s box (memory location). a and b remain distinct boxes (memory locations). The original
value found in a’s box is overwritten when the contents of b’s box are copied into a. After the assignment
of b to a, the reassignment of b to 4 does not affect a.

3.3 Identifiers

While mathematicians are content with giving their variables one-letter names like x, programmers should
use longer, more descriptive variable names. Names such as altitude, sum, and user_name are much
better than the equally permissible a, s, and u. A variable’s name should be related to its purpose within
the program. Good variable names make programs more readable by humans. Since programs often contain
many variables, well-chosen variable names can render an otherwise obscure collection of symbols more
understandable.
C++ has strict rules for variable names. A variable name is one example of an identifier. An identifier
is a word used to name things. One of the things an identifier can name is a variable. We will see in later
chapters that identifiers name other things such as functions and classes. Identifiers have the following form:

• Identifiers must contain at least one character.

• The first character must be an alphabetic letter (upper or lower case) or the underscore

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_

• The remaining characters (if any) may be alphabetic characters (upper or lower case), the underscore,
or a digit

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789

• No other characters (including spaces) are permitted in identifiers.


21
Fundamentals of C++ Programming
3.3. IDENTIFIERS 22

Figure 3.2 How memory changes during variable assignment

a b
int a, b; ? ?
a b
a = 2; 2 ?
a b
b = 5; 2 5

a b
a = b; 5 5
a b
b = 4; 5 4

• A reserved word cannot be used as an identifier (see Table 3.1).

Here are some examples of valid and invalid identifiers:

• All of the following words are valid identifiers and so qualify as variable names: x, x2, total,
port_22, and FLAG.

• None of the following words are valid identifiers: sub-total (dash is not a legal symbol in an
identifier), first entry (space is not a legal symbol in an identifier), 4all (begins with a digit),
#2 (pound sign is not a legal symbol in an identifier), and class (class is a reserved word).

C++ reserves a number of words for special use that could otherwise be used as identifiers. Called
reserved words or keywords, these words are special and are used to define the structure of C++ programs
and statements. Table 3.1 lists all the C++ reserved words.
The purposes of many of these reserved words are revealed throughout this book.
You may not use any of the reserved words in Table 3.1 as identifiers. Fortunately, if you accidentally
attempt to use one of the reserved words in a program as a variable name, the compiler will issue an error
(see Section 4.6 for more on compiler errors).
In Listing 2.1 (simple.cpp) we used several reserved words: using, namespace, and int. Notice
that include, cout, and main are not reserved words.
Some programming languages do not require programmers to declare variables before they are used;
the type of a variable is determined by how the variable is used. Some languages allow the same variable
22
Fundamentals of C++ Programming
3.3. IDENTIFIERS 23

alignas decltype namespace struct


alignof default new switch
and delete noexcept template
and_eq double not this
asm do not_eq thread_local
auto dynamic_cast nullptr throw
bitand else operator true
bitor enum or try
bool explicit or_eq typedef
break export private typeid
case extern protected typename
catch false public union
char float register unsigned
char16_t for reinterpret_cast using
char32_t friend return virtual
class goto short void
compl if signed volatile
const inline sizeof wchar_t
constexpr int static while
const_cast long static_assert xor
continue mutable static_cast xor_eq

Table 3.1: C++ reserved words. C++ reserves these words for specific purposes in program construction. None of the
words in this list may be used as an identifier; thus, you may not use any of these words to name a variable.

to assume different types as its use differs in different parts of a program. Such languages are known as
dynamically-typed languages. C++ is a statically-typed language. In a statically-typed language, the type
of a variable must be explicitly specified before it is used by statements in a program. While the requirement
to declare all variables may initially seem like a minor annoyance, it offers several advantages:

• When variables must be declared, the compiler can catch typographical errors that dynamically-typed
languages cannot detect. For example, consider the following section of code:
int ZERO;
ZER0 = 1;

The identifier in the first line ends with a capital “Oh.” In the second line, the identifier ends with the
digit zero. The distinction may be difficult or impossible to see in a particular editor or printout of
the code. A C++ compiler would immediately detect the typo in the second statement, since ZER0
(last letter a zero) has not been declared. A dynamically-typed language would create two variables:
ZERO and ZER0.
• When variables must be declared, the compiler can catch invalid operations. For example, a variable
may be declared to be of type int, but the programmer may accidentally assign a non-numeric
value to the variable. In a dynamically-typed language, the variable would silently change its type
introducing an error into the program. In C++, the compiler would report the improper assignment
as error, since once declared a C++ variable cannot change its type.
• Ideally, requiring the programmer to declare variables forces the programmer to plan ahead and think
more carefully about the variables a program might require. The purpose of a variable is tied to its
type, so the programmer must have a clear notion of the variable’s purpose before declaring it. When
23
Fundamentals of C++ Programming
3.4. ADDITIONAL INTEGER TYPES 24

variable declarations are not required, a programmer can “make up” variables as needed as the code is
written. The programmer need not do the simple double check of the variable’s purpose that writing
the variable’s declaration requires. While declaring the type of a variable specifies its purpose in only
a very limited way, any opportunity to catch such errors is beneficial.
• Statically-typed languages are generally more efficient than dynamically-typed languages. The com-
piler knows how much storage a variable requires based on its type. The space for that variable’s
value will not change over the life of the variable, since its type cannot change. In a dynamically
typed language that allows a variable to change its type, if a variable’s type changes during program
execution, the storage it requires may change also, so memory for that variable must be allocated
elsewhere to hold the different type. This memory reallocation at run time slows down the program’s
execution.

C++ is a case-sensitive language. This means that capitalization matters. if is a reserved word, but
none of If, IF, or iF are reserved words. Identifiers are case sensitive also; the variable called Name is
different from the variable called name.
Since it can be confusing to human readers, you should not distinguish variables merely by names that
differ in capitalization. For the same reason, it is considered poor practice to give a variable the same name
as a reserved word with one or more of its letters capitalized.

3.4 Additional Integer Types

C++ supports several other integer types. The type short int, which may be written as just short,
represents integers that may occupy fewer bytes of memory than the int type. If the short type occupies
less memory, it necessarily must represent a smaller range of integer values than the int type. The C++
standard does not require the short type to be smaller than the int type; in fact, they may represent the
same set of integer values. The long int type, which may be written as just long, may occupy more
storage than the int type and thus be able to represent a larger range of values. Again, the standard does
not require the long type to be bigger then the int type. Finally, the long long int type, or just
long long, may be larger than a long. The C++ standard guarantees the following relative ranges of
values hold:

short int ≤ int ≤ long int ≤ long long int

On a small embedded device, for example, all of these types may occupy the exact same amount of memory
and, thus, there would be no advantage of using one type over another. On most systems, however, there
will some differences in the ranges.
C++ provides integer-like types that exclude negative numbers. These types include the word unsigned
in their names, meaning they do not allow a negative sign. The unsigned types come in various potential
sizes in the same manner as the signed types. The C++ standard guarantees the following relative ranges of
unsigned values:

unsigned short ≤ unsigned ≤ unsigned long ≤ unsigned long long

Table 3.2 lists the differences among the signed and unsigned integer types in Visual C++. Notice that
the corresponding signed and unsigned integer times occupy the same amount of memory. As a result,
the unsigned types provide twice the range of positive values available to their signed counterparts. For
applications that do not require negative numbers, the unsigned type may be a more appropriate option.
24
Fundamentals of C++ Programming
3.5. FLOATING-POINT TYPES 25

Type Name Short Name Storage Smallest Magnitude Largest Magnitude


short int short 2 bytes −32, 768 32, 767
int int 4 bytes −2, 147, 483, 648 2, 147, 483, 647
long int long 4 bytes −2, 147, 483, 648 2, 147, 483, 647
long long int long long 8 bytes −9, 223, 372, 036, 854, 775, 808 9, 223, 372, 036, 854, 775, 807
unsigned short unsigned short 2 bytes 0 65, 535
unsigned int unsigned 4 bytes 0 4, 294, 967, 295
unsigned long int unsigned long 4 bytes 0 4, 294, 967, 295
unsigned long long int unsigned long long 8 bytes 0 18, 446, 744, 073, 709, 551, 615

Table 3.2: Characteristics of Visual C++ Integer Types

Within the source code, any unadorned numerical literal without a decimal point is interpreted as an
int literal; for example, in the statement
int x = 4456;

the literal value 4456 is an int. In order to represent 4456 as an long, append an L, as in
long x = 4456L;

C++ also permits the lower-case l (elle), as in


long x = 4456l;

but you should avoid it since on many display and printer fonts it looks too much like the digit 1 (one).
Use the LL suffix for long long literals. The suffixes for the unsigned integers are u (unsigned), us
(unsigned short), uL (unsigned long), and uLL (unsigned long long). The capitalization
is unimportant, although capital Ls are preferred.

Within C++ source code all integer literals are int values unless an L or l is
appended to the end of the number; for example, 2 is an int literal, while 2L is
a long literal.

3.5 Floating-point Types

Many computational tasks require numbers that have fractional parts. For example, the formula from mathe-
matics to compute the area of a circle given the circle’s radius, involves the value π, which is approximately
3.14159. C++ supports such non-integer numbers, and they are called floating-point numbers. The name
comes from the fact that during mathematical calculations the decimal point can move or “float” to various
positions within the number to maintain the proper number of significant digits. The types float and
double represent different types of floating-point numbers. The type double is used more often, since it
stands for “double-precision floating-point,” and it can represent a wider range of values with more digits of
precision. The float type represents single-precision floating-point values that are less precise. Table 3.3
provides some information about floating-point values as commonly implemented on 32-bit computer sys-
tems. Floating point numbers can be both positive and negative.
As you can see from Table 3.3, doubles provide more precision at the cost of using more memory.
Listing 3.8 (pi-print.cpp) prints an approximation of the mathematical value π.
25
Fundamentals of C++ Programming
3.5. FLOATING-POINT TYPES 26

Type Storage Smallest Magnitude Largest Magnitude Minimum Precision


float 4 bytes 1.17549 × 10−38 3.40282 × 10+38 6 digits
double 8 bytes 2.22507 × 10−308 1.79769 × 10+308 15 digits
long double 8 bytes 2.22507 × 10−308 1.79769 × 10+308 15 digits

Table 3.3: Characteristics of Floating-point Numbers on 32-bit Computer Systems

Listing 3.8: pi-print.cpp


#include <iostream>

int main() {
double pi = 3.14159;
std::cout << "Pi = " << pi << '\n';
std::cout << "or " << 3.14 << " for short" << '\n';
}

The first line in Listing 3.8 (pi-print.cpp) declares a variable named pi and assigns it a value. The second
line in Listing 3.8 (pi-print.cpp) prints the value of the variable pi, and the third line prints a literal value.
Any literal numeric value with a decimal point in a C++ program automatically has the type double, so
3.14

has type double. To make a literal floating-point value a float, you must append an f or F to the
number, as in
3.14f

(The f or F suffix is used with literal values only; you cannot change a double variable into a float
variable by appending an f. Attempting to do so would change the name of the variable!)

All floating-point literals are double values unless an f or F is appended to the


end of the number; for example, 2.0 is a double literal, while 2.0f is a float
literal.

Floating-point numbers are an approximation of mathematical real numbers. As in the case of the int
data type, the range of floating-point numbers is limited, since each value requires a fixed amount of mem-
ory. In some ways, though, ints are very different from doubles. Any integer within the range of the int
data type can be represented exactly. This is not true for the floating-point types. Consider the real num-
ber π. Since π contains an infinite number of digits, a floating-point number with finite precision can only
approximate its value. Since the number of digits available is limited, even numbers with a finite number
of digits have no exact representation; for example, the number 23.3123400654033989 contains too many
digits for the double type and must be approximated as 23.3023498654034. Section 4.8.2 contains more
information about the consequences of the inexact nature of floating-point numbers.
We can express floating-point numbers in scientific notation. Since most programming editors do not
provide superscripting and special symbols like ×, C++ slightly alters the normal scientific notation. The
number 6.022 × 1023 is written 6.022e23. The number to the left of the e (we can use capital E as well) is
26
Fundamentals of C++ Programming
3.6. CONSTANTS 27

the mantissa, and the number to the right of the e is the exponent of 10. As another example, −5.1 × 10−4
is expressed in C++ as -5.1e-4. Listing 3.9 (scientificnotation.cpp) prints some scientific constants using
scientific notation.

Listing 3.9: scientificnotation.cpp


#include <iostream>

int main() {
double avogadros_number = 6.022e23, c = 2.998e8;
std::cout << "Avogadro's number = " << avogadros_number << '\n';
std::cout << "Speed of light = " << c << '\n';
}

Section 4.8.2 provides some insight into the implementation of C++ floating-point values and explains how
internally all floating-point numbers are stored in exponential notation with a mantissa and exponent.

3.6 Constants

In Listing 3.9 (scientificnotation.cpp), Avogadro’s number and the speed of light are scientific constants; that
is, to the degree of precision to which they have been measured and/or calculated, they do not vary. C++
supports named constants. Constants are declared like variables with the addition of the const keyword:
const double PI = 3.14159;

Once declared and initialized, a constant can be used like a variable in all but one way—a constant may not
be reassigned. It is illegal for a constant to appear on the left side of the assignment operator (=) outside its
declaration statement. A subsequent statement like
PI = 2.5;

would cause the compiler to issue an error message:

error C3892: ’PI’ : you cannot assign to a variable that is const

and fail to compile the program. Since the scientific constants do not change, Listing 3.10 (const.cpp) is a
better version of Listing 3.9 (scientificnotation.cpp).

Listing 3.10: const.cpp


#include <iostream>

int main() {
const double avogadros_number = 6.022e23, c = 2.998e8;
std::cout << "Avogadro's number = " << avogadros_number << '\n';
std::cout << "Speed of light = " << c << '\n';
}

Since it is illegal to assign a constant outside of its declaration statement, all constants must initialized
where they are declared.
By convention, C++ programmers generally express constant names in all capital letters; in this way,
within the source code a human reader can distinguish a constant quickly from a variable.
27
Fundamentals of C++ Programming
3.7. OTHER NUMERIC TYPES 28

3.7 Other Numeric Types

C++ supports several other numeric data types:

• long int—typically provides integers with a greater range than the int type; its abbreviated name
is long. It is guaranteed to provide a range of integer values at least as large as the int type.
An integer literal with a L suffix, as in 19L, has type long. A lower case elle (l) is allowed as a
suffix as well, but you should not use it because it is difficult for human readers to distinguish between
l (lower case elle) and 1 (digit one). (The L suffix is used with literal values only; you cannot change
an int variable into a long by appending an L. Attempting to do so would change the name of the
variable!)

• short int—typically provides integers with a smaller range than the int type; its abbreviated
name is short. It is guaranteed that the range of ints is at least as big as the range of shorts.

• unsigned int—is restricted to nonnegative integers; its abbreviated name is unsigned. While
the unsigned type is limited in nonnegative values, it can represent twice as many positive values
as the int type. (The name int is actually the short name for signed int and int can be written
as signed.)

• long double—can extend the range and precision of the double type.

While the C++ language standard specifies minimum ranges and precision for all the numeric data
types, a particular C++ compiler may exceed the specified minimums.
C++ provides such a variety of numeric types for specialized purposes usually related to building highly
efficient programs. We will have little need to use many of these types. Our examples will use mainly
the numeric types int for integers, double for an approximation of real numbers, and, less frequently,
unsigned when nonnegative integral values are needed.

3.8 Characters

The char data type is used to represent single characters: letters of the alphabet (both upper and lower
case), digits, punctuation, and control characters (like newline and tab characters). Most systems support the
American Standard Code for Information Interchange (ASCII) character set. Standard ASCII can represent
128 different characters. Table 3.4 lists the ASCII codes for various characters.
In C++ source code, characters are enclosed by single quotes ('), as in
char ch = 'A';

Standard (double) quotes (") are reserved for strings, which are composed of characters, but strings and
chars are very different. C++ strings are covered in Section 11.2.6. The following statement would pro-
duce a compiler error message:
ch = "A";

since a string cannot be assigned to a character variable.


Internally, chars are stored as integer values, and C++ permits assigning numeric values to char
variables and assigning characters to numeric variables. The statement
28
Fundamentals of C++ Programming
3.8. CHARACTERS 29

Table 3.4: ASCII codes for characters

ch = 65;

assigns a number to a char variable to show that this perfectly legal. The value 65 is the ASCII code for
the character A. If ch is printed, as in
ch = 65;
std::cout << ch;

the corresponding character, A, would be printed because ch’s declared type is char, not int or some
other numeric type.
Listing 3.11 (charexample.cpp) shows how characters can be used within a program.

Listing 3.11: charexample.cpp


#include <iostream>

int main() {
char ch1, ch2;
ch1 = 65;
ch2 = 'A';
std::cout << ch1 << ", " << ch2 << ", " << 'A' << '\n';
}

The program displays

A, A, A

The first A is printed because the statement


ch1 = 65;

assigns the ASCII code for A to ch1. The second A is printed because the statement
29
Fundamentals of C++ Programming
3.8. CHARACTERS 30

ch2 = 'A';

assigns the literal character A to ch2. The third A is printed because the literal character 'A' is sent directly
to the output stream.
Integers and characters can be freely assigned to each other, but the range of chars is much smaller
than the range of ints, so care must be taken when assigning an int value to a char variable.
Some characters are non-printable characters. The ASCII chart lists several common non-printable
characters:

• '\n'—the newline character

• '\r'—the carriage return character

• '\b'—the backspace character

• '\a'—the “alert” character (causes a “beep” sound or other tone on some systems)

• '\t'—the tab character

• '\f'—the formfeed character

• '\0'—the null character (used in C strings, see Section 11.2.6)

These special non-printable characters begin with a backslash (\) symbol. The backslash is called an
escape symbol, and it signifies that the symbol that follows has a special meaning and should not be inter-
preted literally. This means the literal backslash character must be represented as two backslashes: '\\'.
These special non-printable character codes can be embedded within strings. To embed a backslash
within a string, you must escape it; for example, the statement
std::cout << "C:\\Dev\\cppcode" << '\n';

would print

C:\Dev\cppcode

See what this statement prints:


std::cout << "AB\bCD\aEF" << '\n';

The following two statements behave identically:


std::cout << "End of line" << '\n';
std::cout << "End of line\n";

On the Microsoft Windows platform, the character sequence "\r\n" (carriage return, line feed) appears at
the end of lines in text files. Under Unix and Linux, lines in text files end with '\n' (line feed). On Apple
Macintosh systems, text file lines end with the '\r' (carriage return) character. The compilers that adhere
to the C++ standard will ensure that the '\n' character in a C++ program when sent to the output stream
will produce the correct end-of-line character sequence for the given platform.
30
Fundamentals of C++ Programming
3.9. ENUMERATED TYPES 31

3.9 Enumerated Types

C++ allows a programmer to create a new, very simple type and list all the possible values of that type. Such
a type is called an enumerated type, or an enumeration type. The enum keyword introduces an enumerated
type. The following shows the simplest way to define an enumerated type:
enum Color { Red, Orange, Yellow, Green, Blue, Violet };

Here, the new type is named Color, and a variable of type Color may assume one of the values that
appears in the list of values within the curly braces. The semicolon following the close curly brace is
required. Sometimes the enumerated type definition is formatted as
enum Color {
Red,
Orange,
Yellow,
Green,
Blue,
Violet
};

but the formatting makes no different to the compiler.


The values listed with the curly braces constitute all the values that a variable of the enumerated type
can attain. The name of each value of an enumerated type must be a valid C++ identifier (see Section 3.3).
Given the Color type defined as above, we can declare and use variables of the enum type as shown
by the following code fragment:
Color myColor;
myColor = Orange;

Here the variable myColor has our custom type Color, and its value is Orange.
When declaring enumerated types in this manner it is illegal to reuse an enumerated value name within
another enumerated type within the same program. In the following code, the enumerated value Light
appears in both the Shade type and Weight type:
enum Shade { Dark, Dim, Light, Bright };
enum Weight { Light, Medium, Heavy };

These two enumerated types are incompatible because they share the value Light, and so the compiler
will issue an error.
This style of enumerated type definition is known as an unscoped enumeration. C++ inherits this un-
scoped enumeration style from the C programming language. The C++ standards committee introduced
relatively recently an enhanced way of defining enumerated types known as scoped enumerations, also
known as enumeration classes. Scoped enumerations solve the problem of duplicate enumeration values in
different types. The following definitions are legal within the same program:
enum class Shade { Dark, Dim, Light, Bright };
enum class Weight { Light, Medium, Heavy };

When referring to a value from a scoped enumeration we must prepend the name of its type (class), as in
31
Fundamentals of C++ Programming
3.10. TYPE INFERENCE WITH AUTO 32

Shade color = Shade::Light;


Weight mass = Weight::Light;

In this case Shade and Weight are the scoped enumeration types defined above. Prefixing the type name
to the value with the :: operator enables the compiler to distinguish between the two different values.
Scoped enumerations require the type name prefix even if the program contains no other enumerated types.
In modern C++ development, scoped enumerations are preferable to unscoped enumerations. You should
be familiar with unscoped enumerations, though, as a lot of published C++ code and older C++ books use
unscoped enumerations.
Whether scoped or unscoped, the value names within an enum type must be unique. The convention in
C++ is to captialize the first letter of an enum type and its associated values, although the language does
not enforce this convention.
An enum type is handy for representing a small number of discrete, non-numeric options. For example,
consider a program that controls the movements made by a small robot. The allowed orientations are for-
ward, backward, left, right, up, and down. The program could encode these movements as integers, where
0 means left, 1 means backward, etc. While that implementation will work, it is not ideal. Integers may
assume many more values than just the six values expected. The compiler cannot ensure that an integer
variable representing a robot move will stay in the range 0...5. What if the programmer makes a mistake
and under certain rare circumstances assigns a value outside of the range 0...5? The program then will con-
tain an error that may result in erratic behavior by the robot. With enum types, if the programmer uses only
the named values of the enum type, the compiler will ensure that such a mistake cannot happen.
A particular enumerated type necessarily has far fewer values than a type such as int. Imagine making
an integer enum type and having to list all of its values! (The standard 32-bit int type represents over four
billion values.) Enumerated types, therefore, are practical only for types that have a relatively small range
of values.

3.10 Type Inference with auto

C++ requires that a variable be declared before it is used. Ordinarily this means specifying the variable’s
type, as in
int count;
char ch;
double limit;

A variable may be initialized when it is declared:


int count = 0;
char ch = 'Z';
double limit = 100.0;

Each of the values has a type: 0 is an int, 'Z' is a char, and 0.0 is a double. The auto keyword
allows the compiler to automatically deduce the type of a variable if it is initialized when it is declared:
auto count = 0;
auto ch = 'Z';
auto limit = 100.0;

The auto keyword may not be used without an accompanying initialization; for example, the following
declaration is illegal:
32
Fundamentals of C++ Programming
3.11. EXERCISES 33

auto x;

because the compiler cannot deduce x’s type.

Automatic type inference is supported only by compilers since that comply with
the C++11 standard or above. Programmers using older compilers must specify a
variable’s exact type during the variable’s declaration.

Automatic type deduction with auto is not useful to beginning C++ programmers. It is just as easy to
specify the variable’s type. The value of auto will become clearer when we consider some of the more
advanced features of C++ (see Section 20.2).

3.11 Exercises

1. Will the following lines of code print the same thing? Explain why or why not.
std::cout << 6 << '\n';
std::cout << "6" << '\n';

2. Will the following lines of code print the same thing? Explain why or why not.
std::cout << x << '\n';
std::cout << "x" << '\n';

3. What is the largest int available on your system?


4. What is the smallest int available on your system?
5. What is the largest double available on your system?
6. What is the smallest double available on your system?
7. What C++ data type represents nonnegative integers?
8. What happens if you attempt to use a variable within a program, and that variable is not declared?
9. What is wrong with the following statement that attempts to assign the value ten to variable x?
10 = x;

10. Once a variable has been properly declared and initialized can its value be changed?
11. What is another way to write the following declaration and initialization?
int x = 10;

12. In C++ can you declare more than variable in the same declaration statement? If so, how?
13. In the declaration
int a;
int b;
33
Fundamentals of C++ Programming
3.11. EXERCISES 34

do a and b represent the same memory location?

14. Classify each of the following as either a legal or illegal C++ identifier:

(a) fred
(b) if
(c) 2x
(d) -4
(e) sum_total
(f) sumTotal
(g) sum-total
(h) sum total
(i) sumtotal
(j) While
(k) x2
(l) Private
(m) public
(n) $16
(o) xTwo
(p) _static
(q) _4
(r) ___
(s) 10%
(t) a27834
(u) wilma's

15. What can you do if a variable name you would like to use is the same as a reserved word?

16. Why does C++ require programmers to declare a variable before using it? What are the advantages
of declaring variables?

17. What is the difference between float and double?

18. How can a programmer force a floating-point literal to be a float instead of a double?

19. How is the value 2.45 × 10−5 expressed as a C++ literal?

20. How can you ensure that a variable’s value can never be changed after its initialization?

21. How can you extend the range of int on some systems?

22. How can you extend the range and precision of double on some systems?

23. Write a program that prints the ASCII chart for all the values from 0 to 127.

24. Is "i" a string literal or character literal?

25. Is 'i' a string literal or character literal?


34
Fundamentals of C++ Programming
3.11. EXERCISES 35

26. Is it legal to assign a char value to an int variable?


27. Is it legal to assign an int value to a char variable?
28. What is printed by the following code fragment?
int x;
x = 'A';
std::cout << x << '\n';

29. What is the difference between the character 'n' and the character '\n'?

30. Write a C++ program that simply emits a beep sound when run.
31. Create an unscoped enumeration type that represents the days of the week.
32. Create a scoped enumeration type that represents the days of the week.
33. Create an unscoped enumeration type that represents the months of the year.

34. Create a scoped enumeration type that represents the months of the year.
35. Determine the exact type of each of the following variables:
(a) auto a = 5;
(b) auto b = false;
(c) auto c = 9.3;
(d) auto d = 5.1f;
(e) auto e = 5L;

35
Fundamentals of C++ Programming
3.11. EXERCISES 36

36
Fundamentals of C++ Programming
37

Chapter 4

Expressions and Arithmetic

This chapter uses the C++ numeric types introduced in Chapter 3 to build expressions and perform arith-
metic. Some other important concepts are covered—user input, source formatting, comments, and dealing
with errors.

4.1 Expressions

A literal value like 34 and a properly declared variable like x are examples of simple expressions. We can
use operators to combine values and variables and form more complex expressions. Listing 4.1 (adder.cpp)
shows how the addition operator (+) is used to add two integers.

Listing 4.1: adder.cpp


#include <iostream>

int main() {
int value1, value2, sum;
std::cout << "Please enter two integer values: ";
std::cin >> value1 >> value2;
sum = value1 + value2;
std::cout << value1 << " + " << value2 << " = " << sum << '\n';
}

In Listing 4.1 (adder.cpp):

• int value1, value2, sum;


This statement declares three integer variables, but it does not initialize them. As we examine the rest
of the program we will see that it would be superfluous to assign values to the variables here.
• std::cout << "Please enter two integer values: ";
This statement prompts the user to enter some information. This statement is our usual print state-
ment, but it is not terminated with the end-of-line marker '\n'. This is because we want the cursor
to remain at the end of the printed line so when the user types in values they appear on the same line
as the message prompting for the values. When the user presses the enter key to complete the input,
the cursor will automatically move down to the next line.
37
Fundamentals of C++ Programming
4.1. EXPRESSIONS 38

• std::cin >> value1 >> value2;


This statement causes the program’s execution to stop until the user types two numbers on the key-
board and then presses enter. The first number entered will be assigned to value1, and the second
number entered will be assigned to value2. Once the user presses the enter key, the value entered is
assigned to the variable. The user may choose to type one number, press enter, type the second num-
ber, and press enter again. Instead, the user may enter both numbers separated by one of more spaces
and then press enter only once. The program will not proceed until the user enters two numbers.

The std::cin input stream object can assign values to multiple variables in one
statement, as shown here:
int num1, num2, num3;
std::cin >> num1 >> num2 >> num3;
A common beginner’s mistake is use commas to separate the variables, as in
int num1, num2, num3;
std::cin >> num1, num2, num3;
The compiler will not generate an error message, because it is legal C++ code. The
statement, however, will not assign the three variables from user input as desired.
The comma operator in C++ has different meanings in different contexts, and here
it is treated like a statement separator; thus, the variables num2 and num3 are not
involved with the std::cin input stream object. We will have no need to use
the comma operator in this way, but you should be aware of this potential pitfall.

std::cin is a object that can be used to read input from the user. The >> operator—as used here
in the context of the std::cin object—is known as the extraction operator. Notice that it is “back-
wards” from the << operator used with the std::cout object. The std::cin object represents
the input stream—information flowing into the program from user input from the keyboard. The >>
operator extracts the data from the input stream std::cin and assigns the pieces of the data, in
order, to the various variables on its right.

• sum = value1 + value2;


This is an assignment statement because it contains the assignment operator (=). The variable sum
appears to the left of the assignment operator, so sum will receive a value when this statement exe-
cutes. To the right of the assignment operator is an arithmetic expression involving two variables and
the addition operator. The expression is evaluated by adding together the values of the two variables.
Once the expression’s value has been determined, that value can be assigned to the sum variable.

All expressions have a value. The process of determining the expression’s value is called evaluation.
Evaluating simple expressions is easy. The literal value 54 evaluates to 54. The value of a variable named
x is the value stored in the memory location reserved for x. The value of a more complex expression is
found by evaluating the smaller expressions that make it up and combining them with operators to form
potentially new values.
Table 4.1 lists the main C++ arithmetic operators. Table 4.1. The common arithmetic operations, addi-
tion, subtraction, and multiplication, behave in the expected way. All these operators are classified as binary
operators because they operate on two operands. In the statement
x = y + z;
38
Fundamentals of C++ Programming
4.1. EXPRESSIONS 39

Operator Meaning
+ addition
- subtraction
* multiplication
/ division
% modulus

Table 4.1: The simple C++ arithmetic operators

the right side is an addition expression y + z. The two operands of the + operator are y and z.
Two of the operators above, + and -, serve also as unary operators. A unary operator has only one
operand. The - unary operator expects a single numeric expression (literal number, variable, or complex
numeric expression within parentheses) immediately to its right; it computes the additive inverse of its
operand. If the operand is positive (greater than zero), the result is a negative value of the same magnitude;
if the operand is negative (less than zero), the result is a positive value of the same magnitude. Zero is
unaffected. For example, the following code sequence
int x = 3;
int y = -4;
int z = 0;
std::cout << -x << " " << -y << " " -z << '\n';

within a program would print


-3 4 0

The following statement


std::cout << -(4 - 5) << '\n';

within a program would print


1

The unary + operator is present only for completeness; when applied to a numeric value, variable, or
expression, the resulting value is no different from the original value of its operand. Omitting the unary +
operator from the following statement
x = +y;

does not change the statement’s behavior.


All the arithmetic operators are subject to the limitations of the data types on which they operate; for
example, on a system in which the largest int is 2,147,483,647, the expression
2147483647 + 1

will not evaluate to the correct answer since the correct answer falls outside the range of ints.
If you add, subtract, multiply, is divide two ints, the result is an integer. As long as the operation does
not exceed the range of ints, the arithmetic works as expected. Division, however, is another matter. The
statement
std::cout << 10/3 << " " << 3/10 << '\n';
39
Fundamentals of C++ Programming
4.1. EXPRESSIONS 40

Figure 4.1 Integer division versus integer modulus. Integer division produces the quotient, and modulus
produces the remainder. In this example, 25/3 is 8, and 25%3 is 1.

8 25/3
3)25
-24
1 25%3

prints
3 0

because in the first case 10 divided by 3 is 3 with a remainder of 1, and in the second case 3 divided by
10 is 0 with a remainder of 3. Since integers are whole numbers, any fractional part of the answer must
be discarded. The process of discarding the fractional part leaving only the whole number part is called
truncation. 10 divided by 3 should be 3.3333..., but that value is truncated to 3. Truncation is not rounding;
for example, 11 divided by 3 is 3.6666..., but it also truncates to 3.

Truncation simply removes any fractional part of the value. It does not round.
Both 10.01 and 10.999 truncate to 10.

The modulus operator (%) computes the remainder of integer division; thus,
std::cout << 10%3 << " " << 3%10 << '\n';

prints
1 3

since 10 divided by 3 is 3 with a remainder of 1, and 3 divided by 10 is 0 with a remainder of 3. Figure 4.1
uses long division for a more hands on illustration of how the integer division and modulus operators work.
The modulus operator is more useful than it may first appear. Listing 4.11 (timeconv.cpp) shows how
we can use it to convert a given number of seconds to hours, minutes, and seconds.
In contrast to integer arithmetic, floating-point arithmetic with doubles behaves as expected:
std::cout << 10.0/3.0 << " " << 3.0/10.0 << '\n';

prints
3.33333 0.3
40
Fundamentals of C++ Programming
4.2. MIXED TYPE EXPRESSIONS 41

Figure 4.2 Range of ints versus range of doubles

Range of double
Range of int

+1.8 × 10308 -2.1 × 109 0 +2.1 × 109 +1.8 × 10308

Since a char is stored internally as a number (see Section 3.8), we can perform arithmetic on charac-
ters. We will have little need to apply mathematics to characters, but sometimes it is useful. As an example,
the lower-case letters of the alphabet a–z occupy ASCII values 97–123, with a = 97, b = 98, etc. The upper-
case letters A–Z are coded as 65–91, with A = 65, B = 66, etc. To capitalize any lower-case letter, you need
only subtract 32, as in
char lower = 'd', upper = lower - 32;
std::cout << upper << '\n';

This section of code would print D. If you do not remember the offset of 32 between upper- and lower-case
letter, you can compute it with the letters themselves:
upper = lower - ('a' - 'A');

In this case, if lower has been assigned any value in the range 'a' to 'z', the statement will assign to
upper the capitalized version of lower. On the other hand, if lower’s value is outside of that range,
upper will not receive a meaningful value.

4.2 Mixed Type Expressions

Expressions may contain mixed elements; for example, the following program fragment
int x = 4;
double y = 10.2, sum;
sum = x + y;

adds an int to a double, and the result is being assigned to a double. How is the arithmetic performed?
As shown in Figure 4.2, the range of ints falls completely within the range of doubles; thus, any
int value can represented by a double. The int 4 also can be expressed as the double 4.0. In fact,
since the largest int on most systems is 2,147,483,647, the minimum 15 digits of double precision are
more than adequate to represent all integers exactly. This means that any int value can be represented by
a double. The converse is not true, however. 2,200,000,000 can be represented by a double but it is too
big for the int type. We say that the double type is wider than the int type and that the int type is
narrower than the double type.
It would be reasonable, then, to be able to assign int values to double variables. The process is called
widening, and it is always safe to widen an int to a double. The following code fragment
41
Fundamentals of C++ Programming
4.2. MIXED TYPE EXPRESSIONS 42

double d1;
int i1 = 500;
d1 = i1;
std::cout << "d1 = " << d1 << '\n';

is legal C++ code, and when part of a complete program it would display

d1 = 500

Assigning a double to an int variable is not always possible, however, since the double value may
not be in the range of ints. Furthermore, if the double variable falls within the range of ints but is
not a whole number, the int variable is unable to the manage fractional part. Consider the following code
fragment:
double d = 1.6;
int i = d;

The second line assigns 1 to i. Truncation loses the 0.6 fractional part (see Section 4.1). Note that the as-
signment does not perform proper rounding. The Visual C++ compiler will warn us of a potential problem:

warning C4244: ’=’ : conversion from ’double’ to ’int’, possible loss of data

This warning reminds us that some information may be lost in the assignment. While the compiler and
linker will generate an executable program when warnings are present, you should carefully scrutinize all
warnings. This warning is particularly useful, since it is easy for errors due to the truncation of floating-point
numbers to creep into calculations.
Converting from a wider type to a narrower type (like double to int) is called narrowing. It often is
necessary to assign a floating-point value to an integer variable. If we know the value to assign is within the
range of ints, and the value has no fractional parts or its truncation would do no harm, the assignment is
safe. To perform the assignment without a warning from the compiler, we use a procedure called a cast, also
called a type cast. The cast forces the compiler to accept the assignment without a issuing a warning. The
following statement convinces the compiler to accept the double-to-int assignment without a warning:
i = static_cast<int>(d);

The reserved word static_cast performs the narrowing conversion and silences the compiler warning.
The item to convert (in this case the variable d) is placed in the parentheses, and the desired type (in this
case the type int) appears in the angle brackets. The statement
i = static_cast<int>(d);

does not change the type of the variable d; d is declared to be a double and so must remain a double
variable. The statement makes a copy of d’s value in a temporary memory location, converting it to its
integer representation during the process.
We also can cast literal values and expressions:
i = static_cast<int>(1.6);
i = static_cast<int>(x + 2.1);

42
Fundamentals of C++ Programming
4.2. MIXED TYPE EXPRESSIONS 43

Narrowing a floating-point value to an integer discards any fractional part. Nar-


rowing truncates; it does not round. For example, the double value 1.7 narrows
to the int value 1.

The widening conversion is always safe, so a type cast is not required. Narrowing is a potentially dan-
gerous operation, and using an explicit cast does not remove the danger—it simply silences the compiler.
For example, consider Listing 4.2 (badnarrow.cpp).

Listing 4.2: badnarrow.cpp


#include <iostream>

int main() {
double d = 2200000000.0;
int i = d;
std::cout << "d = " << d << ", i = " << i << '\n';
}

The Visual C++ compiler issues a warning about the possible loss of precision when assigning d to i.
Silencing the warning with a type cast in this case is a bad idea; the program’s output indicates that the
warning should be heeded:

d = 2.2e+09, i = -2147483648

The printed values of i and d are not even close, nor can they be because it is impossible to represent the
value 2,200,000,000 as an int on a system that uses 32-bit integers. When assigning a value of a wider
type to a variable of a narrower type, the programmer must assume the responsibility to ensure that the
actual value to be narrowed is indeed within the range of the narrower type. The compiler cannot ensure the
safety of the assignment.
Casts should be used sparingly and with great care because a cast creates a spot in the program that is
immune to the compiler’s type checking. A careless assignment can produce a garbage result introducing
an error into the program.
When we must perform mixed arithmetic—such as adding an int to a double—the compiler au-
tomatically produces machine language code that copies the int value to a temporary memory location
and transforms it into its double equivalent. It then performs double-precision floating-point arithmetic to
compute the result.
Integer arithmetic occurs only when both operands are ints. 1/3 thus evaluates to 0, but 1.0/3.0,
1/3.0, and 1.0/3 all evaluate to 0.33333.
Since double is wider than int, we say that double dominates int. In a mixed type arithmetic
expression, the less dominant type is coerced into the more dominant type in order to perform the arithmetic
operation.
Section 3.9 introduced enumerated types. Behind the scenes, the compiler translates enumerated values
into integers. The first value in the enumeration is 0, the second value is 1, etc. Even though the underlying
implementation of enumerated types is integer, the compiler does not allow the free exchange between
integers and enumerated types. The following code will not compile:
43
Fundamentals of C++ Programming
4.3. OPERATOR PRECEDENCE AND ASSOCIATIVITY 44

enum class Color { Red, Orange, Yellow, Green, Blue, Violet };


std::cout << Color::Orange << " " << Color::Green << '\n';

The std::cout printing object knows how to print integers, but it does not know anything about our
Color class and its values. If we really want to treat an enumerated type value as its underlying integer,
we must use a type cast. Listing 4.3 (enumcast.cpp) shows how to extract the underlying integer value from
an enumerated type.

Listing 4.3: enumcast.cpp


#include <iostream>

int main() {
enum class Color { Red, Orange, Yellow, Green, Blue, Violet };
std::cout << static_cast<int>(Color::Orange) << " "
<< static_cast<int>(Color::Green) << '\n';
}

Listing 4.3 (enumcast.cpp) prints prints


1 3

This is the expected output because Color::Red is 0, Color::Orange is 1, Color::Yellow is 2,


Color::Green is 3, etc.
Even though enumerated types are encoded as integers internally, programmers may not perform arith-
metic on enumerated types without involving casts. Such opportunities should be very rare; if you need to
perform arithmetic on a variable, it really should be a numerical type, not an enumerated type.

4.3 Operator Precedence and Associativity

When different operators are used in the same expression, the normal rules of arithmetic apply. All C++
operators have a precedence and associativity:

• Precedence—when an expression contains two different kinds of operators, which should be applied
first?

• Associativity—when an expression contains two operators with the same precedence, which should
be applied first?

To see how precedence works, consider the expression


2 + 3 * 4

Should it be interpreted as
(2 + 3) * 4

(that is, 20), or rather is


2 + (3 * 4)
44
Fundamentals of C++ Programming
4.3. OPERATOR PRECEDENCE AND ASSOCIATIVITY 45

(that is, 14) the correct interpretation? As in normal arithmetic, in C++ multiplication and division have
equal importance and are performed before addition and subtraction. We say multiplication and division
have precedence over addition and subtraction. In the expression
2 + 3 * 4

the multiplication is performed before addition, since multiplication has precedence over addition. The
result is 14. The multiplicative operators (*, /, and %) have equal precedence with each other, and the
additive operators (binary + and -) have equal precedence with each other. The multiplicative operators
have precedence over the additive operators.
As in standard arithmetic, in C++ if the addition is to be performed first, parentheses can override the
precedence rules. The expression
(2 + 3) * 4

evaluates to 20. Multiple sets of parentheses can be arranged and nested in any ways that are acceptable in
standard arithmetic.
To see how associativity works, consider the expression
2 - 3 - 4

The two operators are the same, so they have equal precedence. Should the first subtraction operator be
applied before the second, as in
(2 - 3) - 4

(that is, −5), or rather is


2 - (3 - 4)

(that is, 3) the correct interpretation? The former (−5) is the correct interpretation. We say that the subtrac-
tion operator is left associative, and the evaluation is left to right. This interpretation agrees with standard
arithmetic rules. All binary operators except assignment are left associative. Assignment is an exception; it
is right associative. To see why associativity is an issue with assignment, consider the statement
w = x = y = z;

This is legal C++ and is called chained assignment. Assignment can be used as both a statement and an
expression. The statement
x = 2;

assigns the value 2 to the variable x. The expression


x = 2

assigns the value 2 to the variable x and evaluates to the value that was assigned; that is, 2. Since assignment
is right associative, the compiler would interpret the chained assignment example as if it were written as
w = (x = (y = z));

which behaves as follows:

• The expression y = z is evaluated first. z’s value is assigned to y, and the value of the expression
y = z is z’s value.
45
Fundamentals of C++ Programming
4.4. COMMENTS 46

Arity Operators Associativity


Unary +, -
Binary *, /, % Left
Binary +, - Left
Binary = Right

Table 4.2: Operator precedence and associativity. The operators in each row have a higher precedence than the operators
below it. Operators within a row have the same precedence.

• The expression x = (y = z) is evaluated. The value of y = z, that is z, is assigned to x. The


overall value of the expression x = y = z is thus the value of z. Now the values of x, y, and z are
all equal (to z).
• The expression w = (x = y = z) is evaluated. The value of the expression x = y = z is equal
to z’s value, so z’s value is assigned to w. The overall value of the expression w = x = y = z is
equal to z, and the variables w, x, y, and z are all equal (to z).

As in the case of precedence, we can use parentheses to override the natural associativity within an expres-
sion.
The unary operators have a higher precedence than the binary operators, and the unary operators are
right associative. This means the statements
std::cout << -3 + 2 << '\n';
std::cout << -(3 + 2) << '\n';

which display
-1
-5

behave as expected.
Table 4.2 shows the precedence and associativity rules for some C++ operators. The * operator also has
a unary form that has nothing to do with mathematics; it is covered in Section 10.7.

4.4 Comments

Good programmers annotate their code by inserting remarks that explain the purpose of a section of code
or why they chose to write a section of code the way they did. These notes are meant for human readers, not
the compiler. It is common in industry for programs to be reviewed for correctness by other programmers or
technical managers. Well-chosen identifiers (see Section 3.3) and comments can aid this assessment process.
Also, in practice, teams of programmers develop software. A different programmer may be required to finish
or fix a part of the program written by someone else. Well-written comments can help others understand
new code quicker and increase their productivity modifying old or unfinished code. While it may seem
difficult to believe, even the same programmer working on her own code months later can have a difficult
time remembering what various parts do. Comments can help greatly.
Any text contained within comments is ignored by the compiler. C++ supports two types of comments:
single line comments and block comments:

• Single line comment—the first type of comment is useful for writing a single line remark:
46
Fundamentals of C++ Programming
4.5. FORMATTING 47

// Compute the average of the values


avg = sum / number;

The first line here is a comment that comment explains what the statement that follows it is supposed
to do. The comment begins with the double forward slash symbols (//) and continues until the end
of that line. The compiler will ignore the // symbols and the contents of the rest of the line. This
type of comment is also useful for appending a short comment to the end of a statement:
avg = sum / number; // Compute the average of the values

Here, an executable statement and the comment appear on the same line. The compiler will read the
assignment statement here, but it will ignore the comment. The compiler generates the same machine
code for this example as it does for the preceding example, but this example uses one line of source
code instead of two.

• Block comment—the second type of comment begins with the symbols /* and is in effect until
the */ symbols are encountered. The /* . . . */ symbols delimit the comment like parentheses
delimit a parenthetical expression. Unlike parentheses, however, these block comments cannot be
nested within other block comments.
The block comment is handy for multi-line comments:
/* After the computation is completed
the result is displayed. */
std::cout << result << '\n';

What should be commented? Avoid making a remark about the obvious; for example:
result = 0; // Assign the value zero to the variable named result

The effect of this statement is clear to anyone with even minimal C++ programming experience. Thus, the
audience of the comments should be taken into account; generally, “routine” activities require no remarks.
Even though the effect of the above statement is clear, its purpose may need a comment. For example:
result = 0; // Ensures 'result' has a well-defined minimum value

This remark may be crucial for readers to completely understand how a particular part of a program works.
In general, programmers are not prone to providing too many comments. When in doubt, add a remark. The
extra time it takes to write good comments is well worth the effort.

4.5 Formatting

Program comments are helpful to human readers but ignored by the compiler. Another aspect of source code
that is largely irrelevant to the compiler but that people find valuable is its formatting. Imagine the difficulty
of reading a book in which its text has no indentation or spacing to separate one paragraph from another. In
comparison to the source code for a computer program, a book’s organization is quite simple. Over decades
of software construction programmers have established a small collection of source code formatting styles
that the industry finds acceptable.
The compiler allows a lot of leeway for source code formatting. Consider Listing 4.4 (reformattedvariable.cpp)
which is a reformatted version of Listing 3.4 (variable.cpp).
47
Fundamentals of C++ Programming
4.5. FORMATTING 48

Listing 4.4: reformattedvariable.cpp


#include <iostream>
int
main
(
)
{
int
x
;
x
=
10
;
std
::
cout
<<
x
<<
'\n'
;
}

Listing 4.5 (reformattedvariable2.cpp) is another reformatted version of Listing 3.4 (variable.cpp).

Listing 4.5: reformattedvariable2.cpp


#include <iostream>
int main(){int x;x=10;std::cout<<x<<'\n';}

Both reformatted programs are valid C++ and compile to the same machine language code as the original
version. Most would argue that the original version is easier to read and understand more quickly than ei-
ther of the reformatted versions. The elements in Listing 3.4 (variable.cpp) are organized better. Experienced
C++ programmers would find both Listing 4.4 (reformattedvariable.cpp) and Listing 4.5 (reformattedvariable2.cpp)
visually painful.
What are some distinguishing characteristics of Listing 3.4 (variable.cpp)?

• Each statement appears on its own line. A statement is not unnecessarily split between two lines of
text. Visually, one line of text implies one action (statement) to perform.
• The close curly brace aligns vertically with the line above that contains the corresponding open curly
brace. This makes it easier to determine if the curly braces match and nest properly. It also better por-
trays the logical structure of the program. The ability to accurately communicate the logical structure
of a program becomes very important as write more complex programs. Programs with complex logic
frequently use multiple nested curly braces (for example, see Listing 5.12 (troubleshoot.cpp)). With-
out a consistent, organized arrangement of curly braces it can difficult to determine which opening
brace goes with a particular closing brace.
• The statements that constitute the body of main are indented several spaces. This visually empha-
sizes the fact that the elements are indeed logically enclosed. As with curly brace alignment, in-
dentation to emphasize logical enclosure becomes more important as more complex programs are
considered.
48
Fundamentals of C++ Programming
4.5. FORMATTING 49

• Spaces are used to spread out statements and group pieces of the statement. Space around the op-
erators (=) makes it easier to visually separate the operands from the operators and comprehend the
details of the expression. Most people find the statement
total_sale = subtotal + tax;

much easier to read than


total_sale=subtotal+tax;

since the lack of space in the second version makes it more difficult to pick out the individual pieces
of the statement. In the first version with extra space, it is clearer where operators and variable names
begin and end.
In a natural language like English, a book is divided into distinct chapters, and chapters are composed
of paragraphs. One paragraph can be distinguished from another because the first line is indented or
an extra space appears between two paragraphs. Space is used to separate words in each sentence.
Consider how hard it would be to read a book if all the sentences were printed like this one:

Theboyranquicklytothetreetoseethestrandedcat.

Judiciously placed open space in a C++ program can greatly enhance its readability.

C++ gives the programmer a large amount of freedom in formatting source code.
The compiler reads the characters that make up the source code one symbol at a
time left to right within a line before moving to the next line. While extra space
helps readability, spaces are not allowed in some places:
– Variable names and reserved words must appear as unbroken units.
– Multi-symbol operators like << cannot be separated (< < is illegal).

One common coding convention that is universal in C++ programming is demonstrated in Listing 3.10
(const.cpp). While programmers usually use lower-case letters in variable names, they usually express
constant names with all capital letters; for example, PI is used for the mathematical constant π instead of
pi. C++ does not require constants to be capitalized, but capitalizing them aids humans reading the source
code so they can quickly distinguish between variables and constants.
Figure 4.3 shows the four most common ways programmers use indentation and place curly braces in
C++ source code.
The K&R and ANSI styles are the most popular in published C++ source code. The Whitesmith and
Banner styles appear much less frequently. http://en.wikipedia.org/wiki/Indent_style
reviews the various ways to format C++ code. Observe that all the accepted formatting styles indent the
block of statements contained in the main function.
Most software development organizations adopt a set of style guidelines, sometimes called code con-
ventions. These guidelines dictate where to indent and by how many spaces, where to place curly braces,
how to assign names to identifiers, etc. Programmers working for the organization are required to follow
these style guidelines for the code they produce. This better enables any member of the development team
to read and understand more quickly code written by someone else. This is necessary when code is reviewed
for correctness or when code must be repaired or extended, and the original programmer is no longer with
the development team.
49
Fundamentals of C++ Programming
4.6. ERRORS AND WARNINGS 50

Figure 4.3 The most common C++ coding styles.

int main() int main() int main() {


int main() { { {
Body
Body Body Body
}
} } }
K & R Style ANSI Style Whitesmith Style Banner Style

Even if you are not forced to use a particular style, it is important to use a consistent style throughout
the code you write. As our programs become more complex we will need to use additional curly braces
and various levels of indentation to organize the code we write. A consistent style (especially one of the
standard styles shown in Figure 4.3) makes it easier to read and verify that the code actually expresses our
intent. It also makes it easier to find and fix errors. Said another way, haphazard formatting increases the
time it takes to develop correct software because programmer’s mistakes hide better in poorly formatted
code.
Good software development tools can boost programmer productivity, and many programming editors
have the ability to automatically format source code according to a standard style. Some of these editors
can correct the code’s style as the programmer types in the text. A standalone program known as a pretty
printer can transform an arbitrarily formatted C++ source file into a properly formatted one.

4.6 Errors and Warnings

Beginning programmers make mistakes writing programs because of inexperience in programming in gen-
eral or because of unfamiliarity with a programming language. Seasoned programmers make mistakes due
to carelessness or because the proposed solution to a problem is faulty and the correct implementation of an
incorrect solution will not produce a correct program. Regardless of the reason, a programming error falls
under one of three categories:

• compile-time error

• run-time error

• logic error

4.6.1 Compile-time Errors

A compile-time error results from the programmer’s misuse of the language. A syntax error is a common
compile-time error. For example, in English one can say

The boy walks quickly.

This sentence uses correct syntax. However, the sentence


50
Fundamentals of C++ Programming
4.6. ERRORS AND WARNINGS 51

The boy walk quickly.

is not correct syntactically: the number of the subject (singular form) disagrees with the number of the verb
(plural form). It contains a syntax error. It violates a grammatical rule of the English language. Similarly,
the C++ statement
x = y + 2;

is syntactically correct because it obeys the rules for the structure of an assignment statement described in
Section 3.2. However, consider replacing this assignment statement with a slightly modified version:
y + 2 = x;

If a statement like this one appears in a program and the variables x and y have been properly declared, the
compiler will issue an error message; for example, the Visual C++ compiler reports (among other things):

error C2106: ’=’ : left operand must be l-value

The syntax of C++ does not allow an expression like y + 2 to appear on the left side of the assignment
operator.
(The term l-value in the error message refers to the left side of the assignment operator; the l is an
“elle,” not a “one.”.)
The compiler may generate an error for a syntactically correct statement like
x = y + 2;

if either of the variables x or y has not been declared; for example, if y has not been declared, Visual C++
reports:

error C2065: ’y’ : undeclared identifier

Other common compile-time errors include missing semicolons at the end of statements, mismatched curly
braces and parentheses, and simple typographical errors.
Compile-time errors usually are the easiest to repair. The compiler pinpoints the exact location of the
problem, and the error does not depend on the circumstances under which the program executes. The exact
error can be reproduced by simply recompiling the same source code.
Compilers have the reputation for generating cryptic error messages. They seem to provide little help
as far as novice programmers are concerned. Sometimes a combination of errors can lead to messages that
indicate errors on lines that follow the line that contains the actual error. Once you encounter the same
error several times and the compiler messages become more familiar, you become better able to deduce the
actual problem from the reported message. Unfortunately C++ is such a complex language that sometimes a
simple compile-time error can result in a message that is incomprehensible to beginning C++ programmers.

4.6.2 Run-time Errors

The compiler ensures that the structural rules of the C++ language are not violated. It can detect, for exam-
ple, the malformed assignment statement and the use of a variable before its declaration. Some violations of
the language cannot be detected at compile time, however. A program may not run to completion but instead
terminate with an error. We commonly say the program “crashed.” Consider Listing 4.6 (dividedanger.cpp)
which under certain circumstances will crash.
51
Fundamentals of C++ Programming
4.6. ERRORS AND WARNINGS 52

Listing 4.6: dividedanger.cpp


// File dividedanger.cpp

#include <iostream>

int main() {
int dividend, divisor;

// Get two integers from the user


std::cout << "Please enter two integers to divide:";
std::cin >> dividend >> divisor;
// Divide them and report the result
std::cout << dividend << "/" << divisor << " = "
<< dividend/divisor << '\n';
}

The expression
dividend/divisor

is potentially dangerous. If the user enters, for example, 32 and 4, the program works nicely
Please enter two integers to divide: 32 4
32/4 = 8

and displays the answer of 8. If the user instead types the numbers 32 and 0, the program reports an error
and terminates. Division by zero is undefined in mathematics, and integer division by zero in C++ is illegal.
When the program attempts the division at run time, the system detects the attempt and terminates the
program.
This particular program can fail in other ways as well; for example, outside of the C++ world, 32.0
looks like a respectable integer. If the user types in 32.0 and 8, however, the program crashes because 32.0
is not a valid way to represent an integer in C++. When the compiler compiles the source line
std::cin >> dividend >> divisor;

given that dividend has been declared to be an int, it generates slightly different machine language
code than it would if dividend has been declared to be a double instead. The compiled code expects
the text entered by the user to be digits with no extra decoration. Any deviation from this expectation results
in a run-time error. Similar results occur if the user enters text that does not represent an integer, like fred.
Observe that in either case—entry of a valid but inappropriate integer (zero) or entry of a non-integer
(32.0 or fred)—it is impossible for the compiler to check for these problems at compile time. The compiler
cannot predict what the user will enter when the program is run. This means it is up to the programmer
to write code that can handle bad input that the user may provide. As we continue our exploration of
programming in C++, we will discover ways to make our programs more robust against user input (see
Listing 5.3 (betterdivision.cpp) in Chapter 5, for example). The solution involves changing the way the
program runs depending on the actual input provided by the user.

4.6.3 Logic Errors

Consider the effects of replacing the expression


52
Fundamentals of C++ Programming
4.6. ERRORS AND WARNINGS 53

dividend/divisor;

in Listing 4.6 (dividedanger.cpp) with the expression:


divisor/dividend;

The program compiles with no errors. It runs, and unless a value of zero is entered for the dividend, no
run-time errors arise. However, the answer it computes is not correct in general. The only time the correct
answer is printed is when dividend = divisor. The program contains an error, but neither the compiler
nor the run-time system is able detect the problem. An error of this type is known as a logic error.
Listing 4.20 (faultytempconv.cpp) is an example of a program that contains a logic error. Listing 4.20
(faultytempconv.cpp) compiles and does not generate any run-time errors, but it produces incorrect results.
Beginning programmers tend to struggle early on with compile-time errors due to their unfamiliarity
with the language. The compiler and its error messages are actually the programmer’s best friend. As
the programmer gains experience with the language and the programs written become more complicated,
the number of compile-time errors decrease or are trivially fixed and the number of logic errors increase.
Unfortunately, both the compiler and run-time environment are powerless to provide any insight into the
nature and sometimes location of logic errors. Logic errors, therefore, tend to be the most difficult to find
and repair. Tools such as debuggers are frequently used to help locate and fix logic errors, but these tools
are far from automatic in their operation.
Errors that escape compiler detection (run-time errors and logic errors) are commonly called bugs. Since
the compiler is unable to detect these problems, such bugs are the major source of frustration for developers.
The frustration often arises because in complex programs the bugs sometimes only reveal themselves in cer-
tain situations that are difficult to reproduce exactly during testing. You will discover this frustration as your
programs become more complicated. The good news is that programming experience and the disciplined
application of good programming techniques can help reduce the number logic errors. The bad news is that
since software development in an inherently human intellectual pursuit, logic errors are inevitable. Acci-
dentally introducing and later finding and eliminating logic errors is an integral part of the programming
process.

4.6.4 Compiler Warnings

A warning issued by the compiler does mark a violation of the rules in the C++ language, but it is an noti-
fication to the programmer that the program contains a construct that is a potential problem. In Listing 4.10
(tempconv.cpp) the programmer is attempting to print the value of a variable before it has been given a
known value.

Listing 4.7: uninitialized.cpp


// uninitialized.cpp

#include <iostream>

int main() {
int n;
std::cout << n << '\n';
}

An attempt to build Listing 4.7 (uninitialized.cpp) yields the following message from the Visual C++
compiler:
53
Fundamentals of C++ Programming
4.6. ERRORS AND WARNINGS 54

warning C4700: uninitialized local variable ’n’ used

The compiler issued a warning but still generated the executable file. When run, the program produces a
random result because it prints the value in memory associated with the variable, but the program does not
initialize that memory location.
Listing 4.8 (narrow.cpp) assigns a double value to an int variable, which we know from Section 4.1
truncates the result.
Listing 4.8: narrow.cpp
#include <iostream>

int main() {
int n;
double d = 1.6;
n = d;
std::cout << n << '\n';
}

When compiled we see

warning C4244: ’=’ : conversion from ’double’ to ’int’, possible loss of data

Since it is a warning and not an error, the compiler generates the executable, but the warning should prompt
us to stop and reflect about the correctness of the code. The enhanced warning level prevents the program-
mer from being oblivious to the situation.
The default Visual C++ warning level is 3 when compiling in the IDE and level 1 on the command line
(that is why we use the /W3 option on the command line); the highest warning level is 4. You can reduce
the level to 1 or 2 or disable warnings altogether, but that is not recommended. The only reason you might
want to reduce the warning level is to compile older existing C++ source code that does meet newer C++
standards. When developing new code, higher warning levels are preferred since they provide more help to
the programmer. Unless otherwise noted, all the complete program examples in this book compile cleanly
under Visual C++ set at warning level 3. Level 3 is helpful for detecting many common logic errors.
We can avoid most warnings by a simple addition to the code. Section 4.2 showed how we can use
static_cast to coerce a wider type to a narrower type. At Visual C++ warning Level 3, the compiler
issues a warning if the cast is not used. The little code that must be added should cause the programmer to
stop and reflect about the correctness of the construct. The enhanced warning level prevents the programmer
from being oblivious to the situation.

Use the strongest level of warnings available to your compiler. Treat all warnings
as problems that must be corrected. Do not accept as completed a program that
compiles with warnings.

We may assign a double literal to a float variable without any special type casting. The compiler
automatically narrows the double to a float as Listing 4.9 (assignfloat.cpp) shows:

Listing 4.9: assignfloat.cpp


54
Fundamentals of C++ Programming
4.7. ARITHMETIC EXAMPLES 55

#include <iostream>

int main() {
float number;
number = 10.0; // OK, double literal assignable to a float
std::cout << "number = " << number << '\n';
}

The statement
number = 10.0;

assigns a double literal (10.0) to a float variable. You instead may explicitly use a float literal as:
number = 10.0f;

4.7 Arithmetic Examples

The kind of arithmetic to perform in a complex expression is determined on an operator by operator basis.
For example, consider Listing 4.10 (tempconv.cpp) that attempts to convert a temperature from degrees
Fahrenheit to degrees Celsius using the formula

◦ 5
C= × (◦ F − 32)
9

Listing 4.10: tempconv.cpp


// File tempconv.cpp

#include <iostream>

int main() {
double degreesF, degreesC;
// Prompt user for temperature to convert
std::cout << "Enter the temperature in degrees F: ";
// Read in the user's input
std::cin >> degreesF;
// Perform the conversion
degreesC = 5/9*(degreesF - 32);
// Report the result
std::cout << degreesC << '\n';
}

Listing 4.10 (tempconv.cpp) contains comments that document each step explaining the code’s purpose.
An initial test is promising:
Enter the temperature in degrees F: 32
Degrees C = 0

Water freezes at 32 degrees Fahrenheit and 0 degrees Celsius, so the program’s behavior is correct for this
test. Several other attempts are less favorable—consider
55
Fundamentals of C++ Programming
4.7. ARITHMETIC EXAMPLES 56

Enter the temperature in degrees F: 212


Degrees C = 0

Water boils at 212 degrees Fahrenheit which is 100 degrees Celsius, so this answer is not correct.
Enter the temperature in degrees F: -40
Degrees C = 0

The value −40 is the point where the Fahrenheit and Celsius curves cross, so the result should be −40, not
zero. The first test was only coincidentally correct.
Unfortunately, the printed result is always zero regardless of the input. The problem is the division 5/9
in the statement
degreesC = 5/9*(degreesF - 32);

Division and multiplication have equal precedence, and both are left associative; therefore, the division is
performed first. Since both operands are integers, integer division is performed and the quotient is zero (5
divided by 9 is 0, remainder 5). Of course zero times any number is zero, thus the result. The fact that a
floating-point value is involved in the expression (degreesF) and the overall result is being assigned to a
floating-point variable, is irrelevant. The decision about the exact type of operation to perform is made on
an operator-by-operator basis, not globally over the entire expression. Since the division is performed first
and it involves two integer values, integer division is used before the other floating-point pieces become
involved.
One solution simply uses a floating-point literal for either the five or the nine, as in
degreesC = 5.0/9*(degreesF - 32);

This forces a double-precision floating-point division (recall that the literal 5.0 is a double). The correct
result, subject to rounding instead of truncation, is finally computed.
Listing 4.11 (timeconv.cpp) uses integer division and modulus to split up a given number of seconds to
hours, minutes, and seconds.

Listing 4.11: timeconv.cpp


// File timeconv.cpp

#include <iostream>

int main() {
int hours, minutes, seconds;
std::cout << "Please enter the number of seconds:";
std::cin >> seconds;
// First, compute the number of hours in the given number
// of seconds
hours = seconds / 3600; // 3600 seconds = 1 hours
// Compute the remaining seconds after the hours are
// accounted for
seconds = seconds % 3600;
// Next, compute the number of minutes in the remaining
// number of seconds
minutes = seconds / 60; // 60 seconds = 1 minute
// Compute the remaining seconds after the minutes are
56
Fundamentals of C++ Programming
4.7. ARITHMETIC EXAMPLES 57

// accounted for
seconds = seconds % 60;
// Report the results
std::cout << hours << " hr, " << minutes << " min, "
<< seconds << " sec\n";
}

If the user enters 10000, the program prints 2 hr, 46 min, 40 sec. Notice the assignments to
the seconds variable, such as
seconds = seconds % 3600

The right side of the assignment operator (=) is first evaluated. The remainder of seconds divided by
3,600 is assigned back to seconds. This statement can alter the value of seconds if the current value of
seconds is greater than 3,600. A similar statement that occurs frequently in programs is one like
x = x + 1;

This statement increments the variable x to make it one bigger. A statement like this one provides further
evidence that the C++ assignment operator does not mean mathematical equality. The following statement
from mathematics
x = x+1
is surely never true; a number cannot be equal to one more than itself. If that were the case, I would deposit
one dollar in the bank and then insist that I really had two dollars in the bank, since a number is equal to one
more than itself. That two dollars would become 3.00, then 4.00, etc., and soon I would be rich. In C++,
however, this statement simply means “add one to x and assign the result back to x.”
A variation on Listing 4.11 (timeconv.cpp), Listing 4.12 (enhancedtimeconv.cpp) performs the same
logic to compute the time pieces (hours, minutes, and seconds), but it uses more simple arithmetic to
produce a slightly different output—instead of printing 11,045 seconds as 3 hr, 4 min, 5 sec, List-
ing 4.12 (enhancedtimeconv.cpp) displays it as 3:04:05. It is trivial to modify Listing 4.11 (timeconv.cpp)
so that it would print 3:4:5, but Listing 4.12 (enhancedtimeconv.cpp) includes some extra arithmetic to
put leading zeroes in front of single-digit values for minutes and seconds as is done on digital clock displays.

Listing 4.12: enhancedtimeconv.cpp


// File enhancedtimeconv.cpp

#include <iostream>

int main() {
int hours, minutes, seconds;
std::cout << "Please enter the number of seconds:";
std::cin >> seconds;
// First, compute the number of hours in the given number
// of seconds
hours = seconds / 3600; // 3600 seconds = 1 hours
// Compute the remaining seconds after the hours are
// accounted for
seconds = seconds % 3600;
// Next, compute the number of minutes in the remaining
// number of seconds
minutes = seconds / 60; // 60 seconds = 1 minute
// Compute the remaining seconds after the minutes are
57
Fundamentals of C++ Programming
4.8. INTEGERS VERSUS FLOATING-POINT NUMBERS 58

// accounted for
seconds = seconds % 60;
// Report the results
std::cout << hours << ":";
// Compute tens digit of minutes
int tens = minutes / 10;
std::cout << tens;
// Compute ones digit of minutes
int ones = minutes % 10;
std::cout << ones << ":";
// Compute tens digit of seconds
tens = seconds / 10;
std::cout << tens;
// Compute ones digit of seconds
ones = seconds % 10;
std::cout << ones << '\n';
}

Listing 4.12 (enhancedtimeconv.cpp) uses the fact that if x is a one- or two-digit number, x / 10 is the
tens digit of x. If x / 10 is zero, x is necessarily a one-digit number.

4.8 Integers versus Floating-point Numbers

Floating-point numbers offer some distinct advantages over integers. Floating-point numbers, especially
doubles have a much greater range of values than any integer type. Floating-point numbers can have
fractional parts and integers cannot. Integers, however, offer one big advantage that floating-point numbers
cannot—exactness. To see why integers are exact and floating-point numbers are not, we will explore the
way computers store and manipulate the integer and floating-point types.
Computers store all data internally in binary form. The binary (base 2) number system is much simpler
than the familiar decimal (base 10) number system because it uses only two digits: 0 and 1. The decimal
system uses 10 digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9. Despite the lack of digits, every decimal integer has an
equivalent binary representation. Binary numbers use a place value system not unlike the decimal system.
Figure 4.4 shows how the familiar base 10 place value system works.

Figure 4.4 The base 10 place value system

··· 4 7 3 4 0 6
··· 105 104 103 102 101 100
· · · 100,000 10,000 1,000 100 10 1

473, 406 = 4 × 105 + 7 × 104 + 3 × 103 + 4 × 102 + 0 × 101 + 6 × 100


= 400, 000 + 70, 000 + 3, 000 + 400 + 0 + 6
= 473, 406

With 10 digits to work with, the decimal number system distinguishes place values with powers of 10.
Compare the base 10 system to the base 2 place value system shown in Figure 4.5.
58
Fundamentals of C++ Programming
4.8. INTEGERS VERSUS FLOATING-POINT NUMBERS 59

Figure 4.5 The base 2 place value system

··· 1 0 0 1 1 1
··· 25 24 23 22 21 20
··· 32 16 8 4 2 1

1001112 = 1 × 25 + 0 × 24 + 0 × 23 + 1 × 22 + 1 × 21 + 1 × 20
= 32 + 0 + 0 + 4 + 2 + 1
= 39

With only two digits to work with, the binary number system distinguishes place values by powers of
two. Since both binary and decimal numbers share the digits 0 and 1, we will use the subscript 2 to indicate
a binary number; therefore, 100 represents the decimal value one hundred, while 1002 is the binary number
four. Sometimes to be very clear we will attach a subscript of 10 to a decimal number, as in 10010 .
In the decimal system, it is easy to add 3 + 5:
3
+ 5
8
The sum 3 + 9 is a little more complicated, as early elementary students soon discover:
3
+ 9
The answer, of course, is 12, but there is no single digit that means 12—it takes two digits, 1 and 2. The
sum is
1
03
+ 09
12
We can say 3 + 9 is 2, carry the 1. The rules for adding binary numbers are shorter and simpler than decimal
numbers:
02 + 02 = 02
02 + 12 = 12
12 + 02 = 12
12 + 12 = 102
We can say the sum 12 + 12 is 02 , carry the 12 . A typical larger sum would be
11
910 = 10012
+ 310 = 112
1210 = 11002

4.8.1 Integer Implementation

Mathematical integers are whole numbers (no fractional parts), both positive and negative. Standard C++
supports multiple integer types: int, short, long, and long long, unsigned, unsigned short,
59
Fundamentals of C++ Programming
4.8. INTEGERS VERSUS FLOATING-POINT NUMBERS 60

unsigned long, and unsigned long long. These are distinguished by the number of bits required
to store the type, and, consequently, the range of values they can represent. Mathematical integers are
infinite, but all of C++’s integer types correspond to finite subsets of the mathematical integers. The most
commonly used integer type in C++ is int. All ints, regardless of their values, occupy the same amount
of memory and, therefore use the same number of bits. The exact number of bits in an int is processor
specific. A 32-bit processor, for example, is built to manipulate 32-bit integers very efficiently. A C++
compiler for such a system most likely would use 32-bit ints, while a compiler for a 64-bit machine might
represent ints with 64 bits. On a 32-bit computer, the numbers 4 and 1,320,002,912 both occupy 32 bits
of memory.
For simplicity, we will focus on unsigned integers, particularly the unsigned type. The unsigned
type in Visual C++ occupies 32 bits. With 32 bits we can represent 4, 294, 967, 296 different values, and so
Visual C++’s unsigned type represents the integers 0 . . . 4, 294, 967, 295. The hardware in many computer
systems in the 1990s provided only 16-bit integer types, so it was common then for C++ compilers to
support 16-bit unsigned values with a range 0 . . . 65, 535. To simplify our exploration into the properties
of computer-based integers, we will consider an even smaller, mythical unsigned integer type that we will
call unsigned tiny. C++ has no such unsigned tiny type as it has a very small range of values—
too small to be useful as an actual type in real programs. Our unsigned tiny type uses only five bits of
storage, and Table 4.3 shows all the values that a variable of type unsigned tiny can assume.

Binary Bit String Decimal Value


00000 0
00001 1
00010 2
00011 3
00100 4
00101 5
00110 6
00111 7
01000 8
01001 9
01010 10
01011 11
01100 12
01101 13
01110 14
01111 15
10000 16
10001 17
10010 18
10011 19
10100 20
10101 21
10110 22
10111 23
11000 24
11001 25
11010 26
11011 27
11100 28
11101 29
11110 30
11111 31

Table 4.3: The unsigned tiny values

Table 4.3 shows that the unsigned tiny type uses all the combinations of 0s and 1s in five bits. We
can derive the decimal number 6 directly from its bit pattern:

0 0110
00110 =⇒ =⇒ 0 × 16 + 0 × 8 + 1 × 4 + 1 × 2 + 0 × 1 = 6
16 8 4 2 1
60
Fundamentals of C++ Programming
4.8. INTEGERS VERSUS FLOATING-POINT NUMBERS 61

To see that arithmetic works, try adding 4 + 3:


410 = 001002
+ 310 = 000112
710 = 001112
That was easy since involved no carries. Next we will try 3 + 1:
11
310 = 000112
+ 110 = 000012
410 = 001002
In the ones column (rightmost column), 12 + 12 = 102 , so write a 0 and carry the 1 to the top of the next
column to the left (that is, the twos column). In the twos column, 12 + 12 + 02 = 102 , so we must carry a 1
into the fours column as well.
The next example illustrates a limitation of our finite representation. Consider the sum 8 + 28:
11
810 = 010002
+ 2810 = 111002
410 = 1 001002
In the this sum we have a carry of 1 from the eights column to the 16s column, and we have a carry from the
16s column to nowhere. We need a sixth column (a 32s column), another place value, but our unsigned
tiny type is limited to five bits. That carry out from the 16s place is lost. The largest unsigned tiny
value is 31, but 28 + 8 = 36. It is not possible to store the value 36 in an unsigned tiny just as it is
impossible to store the value 5, 000, 000, 000 in a C++ unsigned variable.
Consider exceeding the capacity of the unsigned tiny type by just one:
11111
3110 = 111112
+ 110 = 000012
010 = 1 000002

Adding one to the largest possible unsigned tiny, 31, results in the smallest possible value, 0! This
mirrors the behavior of the actual C++ unsigned type, as Listing 4.13 (unsignedoverflow.cpp) demon-
strates.
Listing 4.13: unsignedoverflow.cpp
#include <iostream>

int main() {
unsigned x = 4294967293; // Almost the largest possible unsigned value
std::cout << x << " + 1 = " << x + 1 << '\n';
std::cout << x << " + 2 = " << x + 2 << '\n';
std::cout << x << " + 3 = " << x + 3 << '\n';
}

Listing 4.13 (unsignedoverflow.cpp) prints


4294967293 + 1 = 4294967294
4294967293 + 2 = 4294967295
4294967293 + 3 = 0
61
Fundamentals of C++ Programming
4.8. INTEGERS VERSUS FLOATING-POINT NUMBERS 62

Figure 4.6 The cyclic nature of 32-bit unsigned integers. Adding 1 to 4,294,967,295 produces 0, one posi-
tion clockwise from 4,294,967295. Subtracting 4 from 2 yields 4,294,967,294, four places counterclockwise
from 2.

0
4,294,967,295 1
2
4,294,967,294
3
4,294,967,293
4
4
4,294,967,292

Subtract Add

2,147,483,650 2,147,483,645

2,147,483,649 2,147,483,646

2,147,483,648 2,147,483,647

In fact, Visual C++’s 32-bit unsigneds follow the cyclic pattern shown in Figure 4.6.
In the figure, an addition moves a value clockwise around the circle, while a subtraction moves a value
counterclockwise around the circle. When the numeric limit is reached, the value rolls over like an automo-
bile odometer. Signed integers exibit a similar cyclic pattern as shown in Figure 4.7.
In the case of signed integers, as Figure 4.7 shows, adding one to the largest representable value pro-
duces the smallest negative value. Listing 4.14 (integeroverflow.cpp) demonstrates.

Listing 4.14: integeroverflow.cpp


#include <iostream>

int main() {
int x = 2147483645; // Almost the largest possible int value
std::cout << x << " + 1 = " << x + 1 << '\n';
std::cout << x << " + 2 = " << x + 2 << '\n';
std::cout << x << " + 3 = " << x + 3 << '\n';
}

Listing 4.14 (integeroverflow.cpp) prints


2147483645 + 1 = 2147483646
2147483645 + 2 = 2147483647
2147483645 + 3 = -2147483648
62
Fundamentals of C++ Programming
4.8. INTEGERS VERSUS FLOATING-POINT NUMBERS 63

Figure 4.7 The cyclic nature of 32-bit signed integers. Adding 1 to 2, 147, 483, 647 produces
−2, 147, 483, 648, one position clockwise from 2, 147, 483, 647. Subtracting 5 from −2, 147, 483, 645 yields
2, 147, 483, 646, five places counterclockwise from −2, 147, 483, 645.

‒1 0
1
‒2 2

‒3 3

4
‒4 4

Subtract Add

‒2,147,483,645 2,147,483,645

‒2,147,483,646 2,147,483,646

‒2,147,483,647 2,147,483,647

‒2,147,483,648

63
Fundamentals of C++ Programming
4.8. INTEGERS VERSUS FLOATING-POINT NUMBERS 64

Attempting to exceed the maximum limit of a numeric type results in overflow, and attempting to exceed
the minimum limit is called underflow. Integer arithmetic that overflow or underflow produces a valid, yet
incorrect integer result. The compiler does not check that a computation will result in exceeding the limit of
a type because it is impossible to do so in general (consider adding two integer variables whose values are
determined at run time). Also significantly, an overflow or underflow situation does not generate a run-time
error. It is, therefore, a logic error if a program performs an integral computation that, either as a final result
or an intermediate value, is outside the range of the integer type being used.

4.8.2 Floating-point Implementation

The standard C++ floating point types consist of float, double, and long double. Floating point
numbers can have fractional parts (decimal places), and the term floating point refers to the fact the dec-
imal point in a number can float left or right as necessary as the result of a calculation (for example,
2.5 × 3.3 = 8.25, two one-decimal place values produce a two-decimal place result). As with the integer
types, the different floating-point types may be distinguished by the number of bits of storage required and
corresponding range of values. The type float stands for single-precision floating-point, and double
stands for double-precision floating-point. Floating point numbers serve as rough approximations of math-
ematical real numbers, but as we shall see, they have some severe limitations compared to actual real
numbers.
On most modern computer systems floating-point numbers are stored internally in exponential form
according to the standard adopted by the Institute for Electrical and Electronic Engineers (IEEE 754). In
the decimal system, scientific notation is the most familiar form of exponential notation:

One mole contains 6.023 × 1023 molecules.

Here 6.023 is called the mantissa, and 23 is the exponent.


The IEEE 754 standard uses binary exponential notation; that is, the mantissa and exponent are binary
numbers. Single-precision floating-point numbers (type float) occupy 32 bits, distributed as follows:

Mantissa 24 bits
Exponent 7 bits
Sign 1 bit

Total 32 bits

Double-precision floating-point numbers (type double) require 64 bits:

Mantissa 52 bits
Exponent 11 bits
Sign 1 bit

Total 64 bits

The details of the IEEE 754 implementation are beyond the scope of this book, but a simplified example
serves to highlight the limitations of floating-point types in general. Recall the fractional place values in the
decimal system. The place values, from left to right, are
64
Fundamentals of C++ Programming
4.8. INTEGERS VERSUS FLOATING-POINT NUMBERS 65

Figure 4.8 A tiny float simplified binary exponential value

. 21 20 21 2
1 0
1 0
22

−1 −2 −3

1 1 1 1
... 10, 000 1, 000 100 10 1 • 10 100 1000 10,000 ...
... 104 103 102 101 100 • 10−1 10−2 10−3 10−4 ...

Each place value is one-tenth the place value to its left. Move to the right, divide by ten; move to the left,
multiply by ten. In the binary system, the factor is two instead of ten:

1 1 1 1
... 16 8 4 2 1 • 2 4 8 16 ...
... 24 23 22 21 20 2 −1 2−2 2 −3 2−4 ...

As in our unsigned tiny example (see Section 4.8.1), consider a a binary exponential number that
consists of only five bits—far fewer bits than either floats or doubles in C++. We will call our mythical
floating-point type tiny float. The first three bits of our 5-bit tiny float type will represent the
mantissa, and the remaining two bits store the exponent. The three bits of the mantissa all appear to the right
of the binary point. The base of the 2-bit exponent is, of course, two. Figure 4.8 illustrates such a value.
To simplify matters even more, neither the mantissa nor the exponent can be negative. Thus, with three
bits, the mantissa may assume one of eight possible values. Since two bits constitute the exponent of tiny
floats, the exponent may assume one of four possible values. Table 4.4 lists all the possible values that
tiny float mantissas and exponents may assume. The number shown in Figure 4.8 is thus

3-bit Mantissas
Bit String Binary Value Decimal Value
0 0 0
000 0.0002 + + = 0.000
2 4 8
0 0 1
001 0.0012 + + = 0.125
2 4 8 2-bit Exponents
0 1 0
010 0.0102 + + = 0.250 Bit String Binary Value Decimal Value
2 4 8
0 1 1 00 2002 20 = 1
011 0.0112 + + = 0.375 01 2012 21 = 2
2 4 8
1 0 0 10 2102 22 = 4
100 0.1002 + + = 0.500
2 4 8 11 2112 23 = 8
1 0 1
101 0.1012 + + = 0.625
2 4 8
1 1 0
110 0.1102 + + = 0.750
2 4 8
1 1 1
111 0.1112 + + = 0.875
2 4 8

Table 4.4: The eight possible mantissas and four possible exponents that make up all tiny float values
65
Fundamentals of C++ Programming
4.8. INTEGERS VERSUS FLOATING-POINT NUMBERS 66

 
−1 −2 −3 (1×21 +0×20 ) 1 1
(1 × 2 + 0×2 + 1×2 )×2 = + × 22
2 8
5
= ×4
8
= 2.5

Table 4.5 combines the mantissas and exponents to reveal all possible tiny float values that we
can represent with the 32 different bit strings made up of five bits. The results are interesting.

Bit String Interpretation Decimal Equivalent Value


00000 .0002 × 2002 0.000 × 1 0.000
00001 .0002 × 2012 0.000 × 2 0.000
00010 .0002 × 2102 0.000 × 4 0.000
00011 .0002 × 2112 0.000 × 8 0.000
00100 .0012 × 2002 0.125 × 1 0.125
00101 .0012 × 2012 0.125 × 2 0.250
00110 .0012 × 2102 0.125 × 4 0.500
00111 .0012 × 2112 0.125 × 8 1.000
01000 .0102 × 2002 0.250 × 1 0.250
01001 .0102 × 2012 0.250 × 2 0.500
01010 .0102 × 2102 0.250 × 4 1.000
01011 .0102 × 2112 0.250 × 8 2.000
01100 .0112 × 2002 0.375 × 1 0.375
01101 .0112 × 2012 0.375 × 2 0.750
01110 .0112 × 2102 0.375 × 4 1.500
01111 .0112 × 2112 0.375 × 8 3.000
10000 .1002 × 2002 0.500 × 1 0.500
10001 .1002 × 2012 0.500 × 2 1.000
10010 .1002 × 2102 0.500 × 4 2.000
10011 .1002 × 2112 0.500 × 8 4.000
10100 .1012 × 2002 0.625 × 1 0.625
10101 .1012 × 2012 0.625 × 2 1.250
10110 .1012 × 2102 0.625 × 4 2.500
10111 .1012 × 2112 0.625 × 8 5.000
11000 .1102 × 2002 0.750 × 1 0.750
11001 .1102 × 2012 0.750 × 2 1.500
11010 .1102 × 2102 0.750 × 4 3.000
11011 .1102 × 2112 0.750 × 8 6.000
11100 .1112 × 2002 0.875 × 1 0.875
11101 .1112 × 2012 0.875 × 2 1.750
11110 .1112 × 2102 0.875 × 4 3.500
11111 .111 2 × 2112 0.875 × 8 7.000

Table 4.5: The tiny float values. The first three bits of the bit string constitute the mantissa, and the last two bits
represent the exponent. Given five bits we can produce 32 different bit strings. Notice that due to the ways different
mantissas and exponents can combine to produce identical values, the 32 different bit strings yield only 20 unique
tiny float values.

The range of our tiny float numbers is 0 . . . 7. Just stating the range is misleading, however, since
1
it might give the impression that we may represent any value in between 0 and 7 down to the th place.
8
This, in fact, is not true. We can represent 2.5 with this scheme, but we have no way of expressing 2.25.
Figure 4.9 plots all the possible tiny float values on the real number line.
Table 4.5 and Figure 4.9 reveal several troubling issues about our tiny float type:

1. There are many gaps; for example, the value 2.4 is missing and thus cannot be represented exactly
(2.5 is the closest approximation). As another example, 0.75 and 1.75 both appear, but 2.75 is missing.
66
Fundamentals of C++ Programming
4.8. INTEGERS VERSUS FLOATING-POINT NUMBERS 67

Figure 4.9 A plot of all the possible tiny float numbers on the real number line. Note that the numbers
are more dense near zero and become more sparse moving to the right. The precision in the range 0 . . . 1
is one-eighth. The precision in the range 1 . . . 2 is only one-fourth, and over the range 2 . . . 4 it drops to
one-half. In the range 4 . . . 7 our tiny float type can represent only whole numbers.

1 1 3 1 5 3 7 1 1 3 1 1
0 8 4 8 2 8 4 8 1 14 12 14 2 22 3 32 4 5 6 7

0 1 2 3 4 5 6 7

2. The scheme duplicates some numbers; for example, three different bit patterns represent the decimal
value 0.5:
0.100 × 200 = 0.010 × 201 = 0.001 × 210 = 0.510

This duplication limits the number of different values that can be represented by a given number of
bits. In our tiny float example 12 of the 32 bit strings (37.5%) are redundant.

3. The numbers are not uniformly dense. There are more values nearer to zero, and the numbers become
more sparse farther away from zero.

Our unsigned tiny type discussed in Section 4.8.1 exhibits none of these weaknesses. All integers
in a given range (0 . . . 31) are present, no two bit strings represent the same value, and the integers are
uniformly distributed across their specified range. While the standard integer types provided by C++ have
much greater ranges than our unsigned tiny type, they all share these same qualities: all values in their
ranges are present, and all bit strings represent unique integer values. The standard floating-point types
provided by C++ use many more bits than our tiny float type, yet they exhibit the same problems
shown to a much smaller degree: missing values, multiple bit patterns representing the same values, and
uneven distribution of values across their ranges. This is not solely a problem of C++’s implementation
of floating-point numbers; all computer languages and hardware that adhere to the IEEE 754 standard
exhibit these problems. To overcome these problems and truly represent and compute with mathematical
real numbers we would need a computer with an infinite amount of memory along with an infinitely fast
processor.
Listing 4.15 (imprecisedifference.cpp) demonstrates the inexactness of floating-point arithmetic.

Listing 4.15: imprecisedifference.cpp


#include <iostream>
#include <iomanip>

int main() {
double d1 = 2000.5;
double d2 = 2000.0;
std::cout << std::setprecision(16) << (d1 - d2) << '\n';
double d3 = 2000.58;
double d4 = 2000.0;
std::cout << std::setprecision(16) << (d3 - d4) << '\n';
}

The output of Listing 4.15 (imprecisedifference.cpp) is:


67
Fundamentals of C++ Programming
4.8. INTEGERS VERSUS FLOATING-POINT NUMBERS 68

0.5
0.5799999999999272

The program uses an additional #include directive:


#include <iomanip>

This preprocessor directive allows us to use the std::setprecision output stream manipulator that
directs the std::cout output stream object to print more decimal places in floating-point values. During
the program’s execution, the first subtraction yields the correct answer. We now know that some floating-
point numbers (like 0.5) have exact internal representations while others are only approximations. The exact
answer for the second subtraction should be 0.58, and if we round the reported result to 12 decimal places,
the answer matches. Floating-point arithmetic often produces results that are close approximations of the
true answer.
Listing 4.16 (precise8th.cpp) computes zero in a roundabout way:

1 1 1 1 1 1 1 1
1− − − − − − − − = 0
8 8 8 8 8 8 8 8

Listing 4.16: precise8th.cpp


#include <iostream>

int main() {
double one = 1.0,
one_eighth = 1.0/8.0,
zero = one - one_eighth - one_eighth - one_eighth
- one_eighth - one_eighth - one_eighth
- one_eighth - one_eighth;

std::cout << "one = " << one << ", one_eighth = " << one_eighth
<< ", zero = " << zero << '\n';

Listing 4.16 (precise8th.cpp) prints


one = 1, one_eighth = 0.125, zero = 0

1
The number has an exact decimal representation, 0.625. It also has an exact binary representation, 0.0012 .
8
1 1
Consider, however, . While = 0.2 has a finite representation in base 10, it has no finite representation in
5 5
base 2:
1
= 0.2 = 0.00110011001100112
5
1
In the binary representation the 00112 bit sequence repeats without end. This means does not have an
5
1
exact floating-point representation. Listing 4.17 (imprecise5th.cpp) illustrates with arithmetic involving .
5
68
Fundamentals of C++ Programming
4.9. MORE ARITHMETIC OPERATORS 69

Listing 4.17: imprecise5th.cpp


#include <iostream>

int main() {
double one = 1.0,
one_fifth = 1.0/5.0,
zero = one - one_fifth - one_fifth - one_fifth
- one_fifth - one_fifth;

std::cout << "one = " << one << ", one_fifth = " << one_fifth
<< ", zero = " << zero << '\n';

one = 1, one_fifth = 0.2, zero = 5.55112e-017

Surely the reported answer (5.551122 × 10−17 = 0.00000000000000005551122) is close to the correct
answer (zero). If you round it to the one-quadrillionth place (15 places behind the decimal point), it is
correct.
What are the ramifications for programmers of this inexactness of floating-point numbers? Section 9.4.6
shows how the misuse of floating-point values can lead to logic errors in programs.
Being careful to avoid overflow and underflow, integer arithmetic is exact and, on most computer sys-
tems, faster than floating-point arithmetic. If an application demands the absolute correct answer and inte-
gers are appropriate for the computation, you should choose integers. For example, in financial calculations
it is important to keep track of every cent. The exact nature of integer arithmetic makes integers an attractive
option. When dealing with numbers, an integer type should be the first choice of programmers.
The limitations of floating-point numbers are unavoidable since computers have finite resources. Com-
promise is inevitable even when we do our best to approximate values with infinite characteristics in a
finite way. Despite their inexactness, double-precision floating-point numbers are used every day through-
out the world to solve sophisticated scientific and engineering problems; for example, the appropriate use
of floating-point numbers have enabled space probes to reach distant planets. In the example C++ programs
above that demonstrate the inexactness of floating-point numbers, the problems largely go away if we agree
that we must compute with the most digits possible and then round the result to fewer digits. Floating-point
numbers provide a good trade-off of precision for practicality.

4.9 More Arithmetic Operators

As Listing 4.12 (enhancedtimeconv.cpp) demonstrates, an executing program can alter a variable’s value
by performing some arithmetic on its current value. A variable may increase by one or decrease by five.
The statement
x = x + 1;

increments x by one, making it one bigger than it was before this statement was executed. C++ has a shorter
statement that accomplishes the same effect:
x++;

This is the increment statement. A similar decrement statement is available:


69
Fundamentals of C++ Programming
4.9. MORE ARITHMETIC OPERATORS 70

x--; // Same as x = x - 1;

These statements are more precisely post-increment and post-decrement operators. There are also pre-
increment and pre-decrement forms, as in
--x; // Same as x = x - 1;
++y; // Same as y = y + 1;

When they appear alone in a statement, the pre- and post- versions of the increment and decrement
operators work identically. Their behavior is different when they are embedded within a more complex
statement. Listing 4.18 (prevspost.cpp) demonstrates how the pre- and post- increment operators work
slightly differently.

Listing 4.18: prevspost.cpp


#include <iostream>

int main() {
int x1 = 1, y1 = 10, x2 = 100, y2 = 1000;
std::cout << "x1=" << x1 << ", y1=" << y1
<< ", x2=" << x2 << ", y2=" << y2 << '\n';
y1 = x1++;
std::cout << "x1=" << x1 << ", y1=" << y1
<< ", x2=" << x2 << ", y2=" << y2 << '\n';
y2 = ++x2;
std::cout << "x1=" << x1 << ", y1=" << y1
<< ", x2=" << x2 << ", y2=" << y2 << '\n';
}

Listing 4.18 (prevspost.cpp) prints


x1=1, y1=10, x2=100, y2=1000
x1=2, y1=1, x2=100, y2=1000
x1=2, y1=1, x2=101, y2=101

If x1 has the value 1 just before the statement


y1 = x1++;

then immediately after the statement executes x1 is 2 and y1 is 1.


If x1 has the value 1 just before the statement
y1 = ++x1;

then immediately after the statement executes x1 is 2 and y1 is also 2.


As you can see, the pre-increment operator uses the new value of the incremented variable when evaluat-
ing the overall expression. In contrast, the post-increment operator uses the original value of the incremented
variable when evaluating the overall expression. The pre- and post-decrement operator behaves similarly.
For beginning programmers it is best to avoid using the increment and decrement operators within more
complex expressions. We will use them frequently as standalone statements since there is no danger of
misinterpreting their behavior when they are not part of a more complex expression.
C++ provides a more general way of simplifying a statement that modifies a variable through simple
arithmetic. For example, the statement
70
Fundamentals of C++ Programming
4.9. MORE ARITHMETIC OPERATORS 71

x = x + 5;

can be shorted to

x += 5;

This statement means “increase x by five.” Any statement of the form

x op= exp;

where

• x is a variable.

• op= is an arithmetic operator combined with the assignment operator; for our purposes, the ones most
useful to us are +=, -=, *=, /=, and %=.

• exp is an expression compatible with the variable x.

Arithmetic reassignment statements of this form are equivalent to

x = x op exp;

This means the statement

x *= y + z;

is equivalent to

x = x * (y + z);

The version using the arithmetic assignment does not require parentheses. The arithmetic assignment is
especially handy if a variable with a long name is to be modified; consider

temporary_filename_length = temporary_filename_length / (y + z);

versus

temporary_filename_length /= y + z;

71
Fundamentals of C++ Programming
4.10. BITWISE OPERATORS 72

Figure 4.10 The bit positions of a 32-bit C++ unsigned integer

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

Do not accidentally reverse the order of the symbols for the arithmetic assignment
operators, like in the statement
x =+ 5;
Notice that the + and = symbols have been reversed. The compiler interprets this
statement as if it had been written
x = +5;
that is, assignment and the unary operator. This assigns x to exactly five instead
of increasing it by five.

Similarly,
x =- 3;
would assign −3 to x instead of decreasing x by three.

Section 4.10 examines some additional operators available in C++.

4.10 Bitwise Operators

In addition to the common arithmetic operators introduced in Section 4.1, C++ provides a few other special-
purpose arithmetic operators. These special operators allow programmers to examine or manipulate the
individual bits that make up data values. They are known as the bitwise operators. These operators consist
of &, |, ^, ∼ , >>, and <<. Applications programmers generally do not need to use bitwise operators very
often, but bit manipulation is essential in many systems programming tasks.
Consider 32-bit unsigned integers. The bit positions usually are numbered right to left, starting with
zero. Figure 4.10 shows how the individual bit positions often are numbered.
The bitwise and operator, &, takes two integer subexpressions and computes an integer result. The
expression e1 & e2 is evaluated as follows:

If bit 0 in both e1 and e2 is 1, then bit 0 in the result is 1; otherwise, bit 0 in the result is 0.

If bit 1 in both e1 and e2 is 1, then bit 1 in the result is 1; otherwise, bit 1 in the result is 0.

If bit 2 in both e1 and e2 is 1, then bit 2 in the result is 1; otherwise, bit 2 in the result is 0.

..
.
72
Fundamentals of C++ Programming
4.10. BITWISE OPERATORS 73

If bit 31 in both e1 and e2 is 1, then bit 31 in the result is 1; otherwise, bit 31 in the result is 0.

For example, the expression 13 & 14 evaluates to 12, since:


1310 = 000000000000000000000000000011012
& 1410 = 000000000000000000000000000011102
1210 = 000000000000000000000000000011002
Bits 2 and 3 are one for both 13 and 14; thus, bits 2 and 3 in the result must be one.
The bitwise or operator, |, takes two integer subexpressions and computes an integer result. The ex-
pression e1 | e2 is evaluated as follows:

If bit 0 in both e1 and e2 is 0, then bit 0 in the result is 0; otherwise, bit 0 in the result is 1.
If bit 1 in both e1 and e2 is 0, then bit 1 in the result is 0; otherwise, bit 1 in the result is 1.
If bit 2 in both e1 and e2 is 0, then bit 2 in the result is 0; otherwise, bit 2 in the result is 1.

..
.
If bit 31 in both e1 and e2 is 0, then bit 31 in the result is 0; otherwise, bit 31 in the result is 1.

For example, the expression 13 | 14 evaluates to 15, since:


1310 = 000000000000000000000000000011012
| 1410 = 000000000000000000000000000011102
1510 = 000000000000000000000000000011112
Bits 4–31 are zero in both 13 and 14. In bits 0–3 either 13 has a one or 14 has a one; therefore, the result
has ones in bits 0–3 and zeroes everywhere else.
The bitwise exclusive or (often refered to as xor) operator (^) takes two integer subexpressions and
computes an integer result. The expression e1 ^ e2 is evaluated as follows:

If bit 0 in e1 is the same as bit 0 in e2 , then bit 0 in the result is 0; otherwise, bit 0 in the result is 1.
If bit 1 in e1 is the same as bit 1 in e2 , then bit 1 in the result is 0; otherwise, bit 1 in the result is 1.
If bit 2 in e1 is the same as bit 2 in e2 , then bit 2 in the result is 0; otherwise, bit 2 in the result is 1.

..
.
If bit 31 in e1 is the same as bit 31 in e2 , then bit 31 in the result is 0; otherwise, bit 31 in the result
is 1.

For example, the expression 13 ^ 14 evaluates to 3, since:


1310 = 000000000000000000000000000011012
ˆ 1410 = 000000000000000000000000000011102
310 = 000000000000000000000000000000112
Bits 0 and 1 differ in 13 and 14, so these bits are one in the result. The bits match in all the other positions,
so these positions must be set to zero in the result.
The bitwise negation operator ( ∼ ) is a unary operator that inverts all the bits of its expression. The
expression ∼ e is evaluated as follows:
73
Fundamentals of C++ Programming
4.10. BITWISE OPERATORS 74

If bit 0 in e is 0, then bit 0 in the result is 1; otherwise, bit 0 in the result is 0.

If bit 1 in e is 0, then bit 1 in the result is 1; otherwise, bit 1 in the result is 0.

If bit 2 in e is 0, then bit 2 in the result is 1; otherwise, bit 2 in the result is 0.

..
.

If bit 31 in e is 0, then bit 31 in the result is 1; otherwise, bit 31 in the result is 0.

For example, the unsigned expression ∼ 13u evaluates to 4, 294, 967, 282, since

1310 = 000000000000000000000000000011012
negate ↓
4, 294, 967, 28210 = 111111111111111111111111111100102

For signed integers the 31st bit stores the number’s sign. Signed integers use a representation called two’s
complement binary, a slight variation of the standard binary layout. Suffice it to say that the int expression
∼ 13 evaluates to the same bit pattern as ∼ 13u, but as a signed integer it represents −14.
The shift operators move all the bits in an integer to the left or right:

• Shift left (<<). The expression x << y, where x and y are integer types, shifts all the bits in x to the left
y places. Zeros fill vacated positions. The bits shifted off the left side are discarded. The expression
5 << 2 evaluates to 20, since 510 = 1012 shifted two places to the left yields 101002 = 2010 .
Observe that x << y is equal to x × 2y .

• Shift right (>>). The expression x >> y, where x and y are integer types, shifts all the bits in x to
the right y places. What fills the vacated bits on the left depends on whether the integer is signed or
unsigned (for example, int versus unsigned):

– For signed values the vacated bit positions are filled with the sign bit (the original leftmost bit).
– For unsigned values the vacated bit positions are filled with zeros.

The bits shifted off the right side are discarded. The expression 5 >> 2 evaluates to 1, since
510 = 1012 shifted two places to the right yields 0012 = 110 (the original bits in positions 1
and 0 are shifted off the right end and lost). Observe that x >> y is equal to x ÷ 2y .

Do not confuse the left shift operator (<<) with the output stream insertion opera-
tor (<<). The operators are identical, but the context differentiates them. If the left
operand is an integer type, << means left shift; if the left operand is a stream out-
put object like std::cout, << means send the right-hand operand to the output
stream object for display.
Similarly, the input stream object std::cin uses the >> for a different purpose
from the right shift operator used with integers.

Listing 4.19 (bitwiseoperators.cpp) experiments with bitwise operators.


74
Fundamentals of C++ Programming
4.10. BITWISE OPERATORS 75

Listing 4.19: bitwiseoperators.cpp


#include <iostream>

int main() {
int x, y;
std::cout << "Please enter two integers: ";
std::cin >> x >> y;
std::cout << x << " & " << y << " = " << (x & y) << '\n';
std::cout << x << " | " << y << " = " << (x | y) << '\n';
std::cout << x << " ^ " << y << " = " << (x ^ y) << '\n';
std::cout << "∼" << x << " = " << ∼x << '\n';
std::cout << x << " << " << 2 << " = " << (x << 2) << '\n';
std::cout << x << " >> " << 2 << " = " << (x >> 2) << '\n';
}

Developers use bitwise operations for a variety of systems-level programming tasks. For example, in a
graphical user interface (GUI), the user generates events by interacting with an application using the mouse
and a keyboard. One event might be clicking a mouse button over a particular graphical element (like a
button) within a window. Multiple pieces of information about this event can be stored in a single integer.
For example, bit 0 may indicate whether or not the ↑ Shift key was held down when the mouse button
was clicked. Bit 1 may be responsible for the Alt key, bit 2 for the Ctrl key, etc. Thus the integer value
5, which in binary is

00000000000000000000000000000101

means that when the mouse button was clicked both the ↑ Shift and Alt keys were being held down.
This might require a different action on the part of the program than if some other combination of keys
(or none) were being pressed. For example, suppose the int variable key_status holds information
about which keys the user was depressing during the most recent mouse click. Consider the expression
key_status & 1. The bit string

00000000000000000000000000000001

represents the expression 1, and the value of key_status is unknown, so key_status & 1 is
key_status = ????????????????????????????????2
& 110 = 000000000000000000000000000000012
010 or 110 = 0000000000000000000000000000000?2

If the answer is zero, this means bit 0 is 0 in key_status, and so the ↑ Shift key is not depressed.
On the other hand, if the answer is one, this means bit 0 is 1, and so the ↑ Shift key is depressed. In the
expression
key_status & 1

the 1 is called a mask, since it serves to mask off, or “remove” the first 31 bits of key_status.
Usually the GUI library will define constants that help programmers examine or modify particular bits;
for example, given the following constants:
const int SHIFT_DOWN = 1; // This is 0...0001
const int CTRL_DOWN = SHIFT_DOWN << 1; // This is 0...0010
const int ALT_DOWN = CTRL_DOWN << 1; // This is 0...0100
75
Fundamentals of C++ Programming
4.11. ALGORITHMS 76

the expression
key_status & 1

is better written
key_status & SHIFT_DOWN

The expression
key_status & (SHIFT_DOWN | ALT_DOWN)

can test for both the ↑ Shift and Alt keys being down during the mouse event. Do you see how the
expression SHIFT_DOWN | ALT_DOWN means both keys are down simultaneously?
We can use masks to ensure that certain bits are on or off. To see how, consider the mask 5, which is

00000000000000000000000000000101

If x is a 32-bit integer variable, we can selectively turn on its bits 0 and 2 with the statement
x = x | 5;

Next, consider the unsigned value 4294967290u, which is ∼ 5u, or

11111111111111111111111111111010

If x is a 32-bit integer variable, we can selectively turn off its bits 0 and 2 with the statement
x = x & 4294967290u;

We cannot fully appreciate the utility of using bitwise operators for masking purposes until we consider
conditional execution in Chapter 5. Even then, since we concentrate on applications programming instead
of systems programming in this book, we will have little use for the bitwise operators except for a few
isolated situations. It is good to be aware of their presence, though, since their accidental use may lead to
difficult to diagnose compiler messages and logic errors.
Like the other C++ arithmetic operators that work on two operands, we may combine the bitwise binary
operators &, |, ^, <<, and >> with assignment to simplify the modification of a variable; for example, the
following statement
x = x | y; // Turn on bits in x determined by

may be written as
x |= y; // Turn on bits in x determined by

Table 4.6 lists the possibilities.

4.11 Algorithms

An algorithm is a finite sequence of steps, each step taking a finite length of time, that solves a problem or
computes a result. A computer program is one example of an algorithm, as is a recipe to make lasagna. In
76
Fundamentals of C++ Programming
4.11. ALGORITHMS 77

Assignment Short Cut


x = x & y; x &= y;
x = x | y; x |= y;
x = x ^ y; x ^= y;
x = x << y; x <<= y;
x = x >> y; x >>= y;

Table 4.6: The bitwise assignment operators

both of these examples, the order of the steps matter. In the case of lasagna, the noodles must be cooked
in boiling water before they are layered into the filling to be baked. It would be inappropriate to place the
raw noodles into the pan with all the other ingredients, bake it, and then later remove the already baked
noodles to cook them in boiling water separately. In the same way, the ordering of steps is very important
in a computer program. While this point may be obvious, consider the following sound argument:

1. The relationship between degrees Celsius and degrees Fahrenheit can be expressed as

◦ 5
C= × (◦ F − 32)
9

2. Given a temperature in degrees Fahrenheit, the corresponding temperature in degrees Celsius can be
computed.

Armed with this knowledge, Listing 4.20 (faultytempconv.cpp) follows directly.

Listing 4.20: faultytempconv.cpp


// File faultytempconv.cpp

#include <iostream>

int main() {
double degreesF = 0, degreesC = 0;
// Define the relationship between F and C
degreesC = 5.0/9*(degreesF - 32);
// Prompt user for degrees F
std::cout << "Enter the temperature in degrees F: ";
// Read in the user's input
std::cin >> degreesF;
// Report the result
std::cout << degreesC << '\n';
}

Unfortunately, the executing program always displays


-17.7778

regardless of the input provided. The English description provided above is correct. No integer division
problems lurk, as in Listing 4.10 (tempconv.cpp). The problem lies simply in statement ordering. The state-
ment
degreesC = 5.0/9*(degreesF - 32);
77
Fundamentals of C++ Programming
4.12. EXERCISES 78

is an assignment statement, not a definition of a relationship that exists throughout the program. At the
point of the assignment, degreesF has the value of zero. The executing program computes and assigns
the degreesC variable before receiving degreesF’s value from the user.
As another example, suppose x and y are two integer variables in some program. How would we
interchange the values of the two variables? We want x to have y’s original value and y to have x’s original
value. This code may seem reasonable:
x = y;
y = x;

The problem with this section of code is that after the first statement is executed, x and y both have the same
value (y’s original value). The second assignment is superfluous and does nothing to change the values of
x or y. The solution requires a third variable to remember the original value of one the variables before it
is reassigned. The correct code to swap the values is
temp = x;
x = y;
y = temp;

This small example emphasizes the fact that algorithms must be specified precisely. Informal notions about
how to solve a problem can be valuable in the early stages of program design, but the coded program
requires a correct detailed description of the solution.
The algorithms we have seen so far have been simple. Statement 1, followed by Statement 2, etc. until
every statement in the program has been executed. Chapter 5 and Chapter 6 introduce some language
constructs that permit optional and repetitive execution of some statements. These constructs allow us to
build programs that do much more interesting things, but more complex algorithms are required to make it
happen. We must not lose sight of the fact that a complicated algorithm that is 99% correct is not correct.
An algorithm’s design and implementation can be derailed by inattention to the smallest of details.

4.12 Exercises

1. Is the literal 4 a valid C++ expression?

2. Is the variable x a valid C++ expression?

3. Is x + 4 a valid C++ expression?

4. What affect does the unary + operator have when applied to a numeric expression?

5. Sort the following binary operators in order of high to low precedence: +, -, *, /, %, =.

6. Write a C++ program that receives two integer values from the user. The program then should print
the sum (addition), difference (subtraction), product (multiplication), quotient (division), and remain-
der after division (modulus). Your program must use only integers.
A sample program run would look like (the user enters the 10 and the 2 after the colons, and the
program prints the rest):
Please enter the first number: 10
Please enter the second number: 2
10 + 2 = 12
10 - 2 = 8
78
Fundamentals of C++ Programming
4.12. EXERCISES 79

10 * 2 = 20
10 / 2 = 5
10 % 2 = 0

Can you explain the results it produces for all of these operations?
7. Write a C++ program that receives two double-precision floating-point values from the user. The
program then should print the sum (addition), difference (subtraction), product (multiplication), and
quotient (division). Your program should use only integers.
A sample program run would look like (the user enters the 10 and the 2.5 after the colons, and the
program prints the rest):
Please enter the first number: 10
Please enter the second number: 2.5
10 + 2.5 = 12.5
10 - 2.5 = 7.5
10 * 2.5 = 25
10 / 2.5 = 4

Can you explain the results it produces for all these operations? What happens if you attempt to
compute the remainder after division (modulus) with double-precision floating-point values?
8. Given the following declaration:
int x = 2;

Indicate what each of the following C++ statements would print.


(a) std::cout << "x"<< '\n';
(b) std::cout << 'x'<< '\n';
(c) std::cout << x << '\n';
(d) std::cout << "x + 1"<< '\n';
(e) std::cout << 'x'+ 1 << '\n';
(f) std::cout << x + 1 << '\n';
9. Sort the following types in order from narrowest to widest: int, double, float, long, char.
10. Given the following declarations:
int i1 = 2, i2 = 5, i3 = -3;
double d1 = 2.0, d2 = 5.0, d3 = -0.5;

Evaluate each of the following C++ expressions.


(a) i1 + i2
(b) i1 / i2
(c) i2 / i1
(d) i1 * i3
(e) d1 + d2
(f) d1 / d2
(g) d2 / d1
79
Fundamentals of C++ Programming
4.12. EXERCISES 80

(h) d3 * d1
(i) d1 + i2
(j) i1 / d2
(k) d2 / i1
(l) i2 / d1
(m) i1/i2*d1
(n) d1*i1/i2
(o) d1/d2*i1
(p) i1*d1/d2
(q) i2/i1*d1
(r) d1*i2/i1
(s) d2/d1*i1
(t) i1*d2/d1

11. What is printed by the following statement:


std::cout << /* 5 */ 3 << '\n';

12. Given the following declarations:


int i1 = 2, i2 = 5, i3 = -3;
double d1 = 2.0, d2 = 5.0, d3 = -0.5;

Evaluate each of the following C++ expressions.

(a) i1 + (i2 * i3)


(b) i1 * (i2 + i3)
(c) i1 / (i2 + i3)
(d) i1 / i2 + i3
(e) 3 + 4 + 5 / 3
(f) (3 + 4 + 5) / 3
(g) d1 + (d2 * d3)
(h) d1 + d2 * d3
(i) d1 / d2 - d3
(j) d1 / (d2 - d3)
(k) d1 + d2 + d3 / 3
(l) (d1 + d2 + d3) / 3
(m) d1 + d2 + (d3 / 3)
(n) 3 * (d1 + d2) * (d1 - d3)

13. How are single-line comments different from block comments?

14. Can block comments be nested?

15. Which is better, too many comments or too few comments?


80
Fundamentals of C++ Programming
4.12. EXERCISES 81

16. What is the purpose of comments?

17. The programs in Listing 3.4 (variable.cpp), Listing 4.4 (reformattedvariable.cpp), and Listing 4.5
(reformattedvariable2.cpp) compile to the same machine code and behave exactly the same. What
makes one of the programs clearly better than the others?

18. Why is human readability such an important consideration?

19. Consider the following program which contains some errors. You may assume that the comments
within the program accurately describe the program’s intended behavior.
#include <iostream>

int main() {
int n1, n2, d1; // 1
// Get two numbers from the user
cin << n1 << n2; // 2
// Compute sum of the two numbers
std::cout << n1 + n2 << '\n'; // 3
// Compute average of the two numbers
std::cout << n1+n2/2 << '\n'; // 4
// Assign some variables
d1 = d2 = 0; // 5
// Compute a quotient
std::cout << n1/d1 << '\n'; // 6
// Compute a product
n1*n2 = d1; // 7
// Print result
std::cout << d1 << '\n'; // 8
}

For each line listed in the comments, indicate whether or not a compile-time, run-time, or logic error
is present. Not all lines contain an error.

20. What distinguishes a compiler warning from a compiler error? Should you be concerned about warn-
ings? Why or why not?

21. What are the advantages to enhancing the warning reporting capabilities of the compiler?

22. Write the shortest way to express each of the following statements.

(a) x = x + 1;
(b) x = x / 2;
(c) x = x - 1;
(d) x = x + y;
(e) x = x - (y + 7);
(f) x = 2*x;
(g) number_of_closed_cases = number_of_closed_cases + 2*ncc;

23. What is printed by the following code fragment?


81
Fundamentals of C++ Programming
4.12. EXERCISES 82

int x1 = 2, y1, x2 = 2, y2;


y1 = ++x1;
y2 = x2++;
std::cout << x1 << " " << x2 << '\n';
std::cout << y1 << " " << y2 << '\n';

Why does the output appear as it does?

24. Consider the following program that attempts to compute the circumference of a circle given the
radius entered by the user. Given a circle’s radius, r, the circle’s circumference, C is given by the
formula:

C = 2πr

#include <iostream>

int main() {
double C, r;
const double PI = 3.14159;
// Formula for the area of a circle given its radius
C = 2*PI*r;
// Get the radius from the user
cout >> "Please enter the circle's radius: ";
cin << r;
// Print the circumference
std::cout << "Circumference is " << C << '\n';
}

(a) The compiler issues a warning. What is the warning?


(b) The program does not produce the intended result. Why?
(c) How can it be repaired so that it not only eliminates the warning but also removes the logic
error?

25. In mathematics, the midpoint between the two points (x1 , y1 ) and (x2 , y2 ) is computed by the formula
 
x1 + x2 y1 + y2
,
2 2

Write a C++ program that receives two mathematical points from the user and computes and prints
their midpoint.
A sample run of the program produces
Please enter the first point: (0,0)
Please enter the second point: (1,1)
The midpoint of (0,0) and (1,1) is (0.5,0.5)

The user literally enters "(0,0)" and "(1,1)" with the parentheses and commas as shown. To see how to
do this, suppose you want to allow a user to enter the point (2.3, 9), assigning the x component of the
point to a variable named x and the y component to a variable named y. You can add the following
code fragment to your program to achieve the desired effect:
82
Fundamentals of C++ Programming
4.12. EXERCISES 83

Food Calories
Bean burrito 357
Salad w/dressing 185
Milkshake 388

Table 4.7: Calorie content of several fast food items

double x, y;
char left_paren, comma, right_paren;
std::cin >> left_paren >> x >> comma >> y >> right_paren;

If the user literally types (2.3,9), the std::cin statement will assign the ( character to the
variable left_paren. It next will assign 2.3 to the variable x. It assigns the , character to the
variable named comma, the value 9 to the y variable, and the ) character to the right_paren
variable. The left_paren, comma, and right_paren variables are just placeholders for the
user’s input and are not used elsewhere within the program. In reality, the user can type in other
characters in place of the parentheses and comma as long as the numbers are in the proper location
relative to the characters; for example, the user can type *2.3:9#, and the program will interpret
the input as the point (2.3, 9).
26. Table 4.7 lists the Calorie contents of several foods. Running or walking burns off about 100 Calories
per mile. Write a C++ program that requests three values from the user: the number of bean burritos,
salads, and shakes consumed (in that order). The program should then display the number of miles
that must be run or walked to burn off the Calories represented in that food. The program should run
as follows (the user types in the 3 2 1):
Number of bean burritos, bowls of salad, and milkshakes eaten? 3 2 1

You ingested 1829 Calories


You will have to run 18.29 miles to expend that much energy

Observe that the result is a floating-point value, so you should use floating-point arithmetic to com-
pute the answers for this problem.

83
Fundamentals of C++ Programming
4.12. EXERCISES 84

84
Fundamentals of C++ Programming
85

Chapter 5

Conditional Execution

All the programs in the preceding chapters execute exactly the same statements regardless of the input, if
any, provided to them. They follow a linear sequence: Statement 1, Statement 2, etc. until the last statement
is executed and the program terminates. Linear programs like these are very limited in the problems they
can solve. This chapter introduces constructs that allow program statements to be optionally executed,
depending on the context (input) of the program’s execution.

5.1 Type bool

Arithmetic expressions evaluate to numeric values; a Boolean expression, evaluates to true or false.
While Boolean expressions may appear very limited on the surface, they are essential for building more
interesting and useful programs.
C++ supports the non-numeric data type bool, which stands for Boolean. The term Boolean comes
from the name of the British mathematician George Boole. A branch of discrete mathematics called Boolean
algebra is dedicated to the study of the properties and the manipulation of logical expressions. Compared to
the numeric types, the bool type is very simple in that it can represent only two values: true or false.
Listing 5.1 (boolvars.cpp) is a simple program demonstrating the use of Boolean variables.

Listing 5.1: boolvars.cpp


#include <iostream>

int main() {
// Declare some Boolean variables
bool a = true, b = false;
std::cout << "a = " << a << ", b = " << b << '\n';
// Reassign a
a = false;
std::cout << "a = " << a << ", b = " << b << '\n';
// Mix integers and Booleans
a = 1;
b = 1;
std::cout << "a = " << a << ", b = " << b << '\n';
// Assign Boolean value to an integer
int x = a, y = true;
85
Fundamentals of C++ Programming
5.1. TYPE BOOL 86

std::cout << "a = " << a << ", b = " << b


<< ", x = " << x << ", y = " << y << '\n';
// More mixing
a = 1725; // Warning issued
b = -19; // Warning issued
std::cout << "a = " << a << ", b = " << b << '\n';
}

Listing 5.1 (boolvars.cpp) produces the following output:


a = 1, b = 0
a = 0, b = 0
a = 1, b = 1
a = 1, b = 1, x = 1, y = 1
a = 1, b = 1

As you can see from running Listing 5.1 (boolvars.cpp), the Boolean values false and true are
represented as integer 0 and integer 1. More precisely, zero represents the bool value false, and any
non-zero integer (positive or negative) means true. The direct assignment to a bool variable of an integer
other than 0 or 1 may result in a warning (Visual C++ reports truncation of ’int’ to ’bool’), but the variable
is still interpreted as true. The data type bool is basically a convenience for programmers; any C++
program that uses bool variables can be rewritten using integers instead to achieve the same results. While
Boolean values and variables are freely compatible and interchangeable with integers, the bool type is
convenient and should be used when the context involves truth values instead of numbers.
Sometimes it is desirable to print Boolean values with the words false and true rather than with 0 and
1. Listing 5.2 (boolalpha.cpp) uses the std::boolalpha stream manipulator to condition the output
stream to provide the designed effect.

Listing 5.2: boolalpha.cpp


#include <iostream>

int main() {
// Declare some Boolean variables
bool a = true, b = false;
// Condition the stream to display Booleans as words
std::cout << std::boolalpha;
std::cout << "a = " << a << ", b = " << b << '\n';
// Reassign a
a = false;
std::cout << "a = " << a << ", b = " << b << '\n';
// Mix integers and Booleans
a = 1;
b = 1;
std::cout << "a = " << a << ", b = " << b << '\n';
// Assign Boolean value to an integer
int x = a, y = true;
std::cout << "a = " << a << ", b = " << b
<< ", x = " << x << ", y = " << y << '\n';
// More mixing
a = 1725; // Warning issued
b = -19; // Warning issued
std::cout << "a = " << a << ", b = " << b << '\n';
}
86
Fundamentals of C++ Programming
5.2. BOOLEAN EXPRESSIONS 87

Operator Meaning
== Equal to
< Less than
> Greater than
<= Less than or equal to
>= Greater than or equal to
!= Not equal to

Table 5.1: C++ Relational operators

Expression Value
10 < 20 always true
10 >= 20 always false
x == 10 true only if x has the value 10
X != y true unless x and y have the same values

Table 5.2: Relational operator examples

Listing 5.2 (boolalpha.cpp) produces the following output:


a = true, b = false
a = false, b = false
a = true, b = true
a = true, b = true, x = 1, y = 1
a = true, b = true

It is important to note that the Visual C++ compiler issues warnings for the last two assignment state-
ments in Listing 5.1 (boolvars.cpp). Even though any non-zero value is considered true, 1 is the preferred
integer equivalent to true (as you can see when you attempt to print the literal value true). Since the
need to assign to a Boolean variable a value other than true or false or the equivalent 1 or 0 should be
extremely rare, the compiler’s message alerts the programmer to check to make sure the assignment is not
a mistake.

5.2 Boolean Expressions

The simplest Boolean expressions are false and true, the Boolean literals. A Boolean variable is also
a Boolean expression. An expression comparing numeric expressions for equality or inequality is also a
Boolean expression. The simplest kinds of Boolean expressions use relational operators to compare two
expressions. Table 5.1 lists the relational operators available in C++.
Table 5.2 shows some simple Boolean expressions with their associated values. An expression like
10 < 20 is legal but of little use, since the expression true is equivalent, simpler, and less likely to
confuse human readers. Boolean expressions are extremely useful when their truth values depend on the
values of one or more variables.
The relational operators are binary operators and are all left associative. They all have a lower prece-
dence than any of the arithmetic operators; therefore, the expression
x + 2 < y / 10

87
Fundamentals of C++ Programming
5.3. THE SIMPLE IF STATEMENT 88

is evaluated as if parentheses were placed as so:


(x + 2) < (y / 10)

C++ allows statements to be simple expressions; for example, the statement


x == 15;
may look like an attempt to assign the value 15 to the variable x, but it is not.
The = operator performs assignment, but the == operator checks for relational
equality. If you make a mistake and use == as shown here, Visual C++ will issue
a warning that includes the message
warning C4553: ’==’ : operator has no effect; did you intend ’=’?
Recall from Section 4.6.4 that a compiler warning does not indicate a violation of
the rules of the language; rather it alerts the programmer to a possible trouble spot
in the code.

Another example of an expression used as a statement is


x + 15;
This statement is a legal (but useless) C++ statement, and the compiler notifies us
accordingly:
warning C4552: ’+’ : operator has no effect; expected operator with
side-effect

Why are expressions allowed as statements? Some simple expressions have side
effects that do alter the behavior of the program. One example of such an ex-
pression is x++. Listing 4.18 (prevspost.cpp) showed how x++ behaves both as a
standalone statement and as an expression within a larger statement. A more com-
mon example is the use of a function call (which is an expression) as standalone
a statement. (We introduce functions in Chapter 8.) In order to keep the structure
of the language as uniform as possible, C++ tolerates useless expressions as state-
ments to enable programmers to use the more useful expression-statements. For-
tunately, most compilers issue informative warnings about the useless expression-
statements to keep developers on track.

5.3 The Simple if Statement

The Boolean expressions described in Section 5.2 at first may seem arcane and of little use in practical
programs. In reality, Boolean expressions are essential for a program to be able to adapt its behavior at
run time. Most truly useful and practical programs would be impossible without the availability of Boolean
expressions.
The run-time exceptions mentioned in Section 4.6 arise from logic errors. One way that Listing 4.6
(dividedanger.cpp) can fail is when the user enters a zero for the divisor. Fortunately, programmers can take
steps to ensure that division by zero does not occur. Listing 5.3 (betterdivision.cpp) shows how it might be
done.
88
Fundamentals of C++ Programming
5.3. THE SIMPLE IF STATEMENT 89

Listing 5.3: betterdivision.cpp


#include <iostream>

int main() {
int dividend, divisor;

// Get two integers from the user


std::cout << "Please enter two integers to divide:";
std::cin >> dividend >> divisor;
// If possible, divide them and report the result
if (divisor != 0)
std::cout << dividend << "/" << divisor << " = "
<< dividend/divisor << '\n';
}

The second std::cout statement may not always be executed. In the following run
Please enter two integers to divide: 32 8
32/8 = 4

it is executed, but if the user enters a zero as the second number:


Please enter two integers to divide: 32 0

the program prints nothing after the user enters the values.
The last statement in Listing 5.3 (betterdivision.cpp) begins with the reserved word if. The if state-
ment allows code to be optionally executed. In this case, the printing statement is executed only if the
variable divisor’s value is not zero.
The Boolean expression
divisor != 0

determines if the single statement that follows the right parenthesis is executed. If divisor is not zero,
the message is printed; otherwise, the program prints nothing.
Figure 5.1 shows how program execution flows through the if statement. of Listing 5.3 (betterdivision.cpp).

The general form of a simple if statement is

if ( condition )

statement

• The reserved word if begins the if statement.


• The Boolean expression condition determines whether or not the body will be executed. The Boolean
expression must be enclosed within parentheses as shown.
89
Fundamentals of C++ Programming
5.3. THE SIMPLE IF STATEMENT 90

Figure 5.1 if flowchart

Is no
divisor ≠ 0?

yes

do the division
and print result

90
Fundamentals of C++ Programming
5.3. THE SIMPLE IF STATEMENT 91

• The statement is the statement to be executed if the Boolean expression is true. The statement makes
up the body of the if statement. Section 5.4 shows how the body can be composed of multiple
statements.

Good coding style dictates we should indent the body to emphasize the optional execution and improve
the program’s readability. The compiler does not require the indentation. Sometimes programmers will
place a one-statement body on the same line as the if; for example, the following if statement optionally
assigns y:

if (x < 10)
y = x;

and could be written as

if (x < 10) y = x;

but should not be written as

if (x < 10)
y = x;

because the lack of indentation hides the fact that the program optionally executes the assignment statement.
The compiler will accept it, but it is misleading to human readers accustomed to the indentation convention.
The compiler, of course, will accept the code written as

if(x<10)y=x;

but the lack of spaces makes it difficult for humans to read.

When the if statement is written the preferred way using two lines of source
code, it is important not to put a semicolon at the end of the first line:
if (x < 10); // No! Don't do this!
y = x;
Here, the semicolon terminates the if statement, but the indentation implies that
the second line is intended to be the body of the if statement. The compiler,
however, interprets the badly formatted if statement as if it were written as
if (x < 10)
; // This is what is really going on.
y = x;
This is legal in C++; it means the if statement has an empty body. In which
case the assignment is not part of the body. The assignment statement is after the
body and always will be executed regardless of the truth value of the Boolean
expression.

91
Fundamentals of C++ Programming
5.4. COMPOUND STATEMENTS 92

When checking for equality, as in


if (x == 10)
std::cout << "ten";
be sure to use the relational equality operator (==), not the assignment operator
(=). Since an assignment statement has a value (the value that is assigned, see Sec-
tion 4.3), C++ allows = within the conditional expression. It is, however, almost
always a mistake when beginning programmers use = in this context. Visual C++
at warning Level 4 checks for the use of assignment within a conditional expres-
sion; the default Level 3 does not.

5.4 Compound Statements

Sometimes you need to optionally execute more than one statement based on a particular condition. List-
ing 5.4 (alternatedivision.cpp) shows how you must use curly braces to group multiple statements together
into one compound statement.

Listing 5.4: alternatedivision.cpp


#include <iostream>

int main() {
int dividend, divisor, quotient;

// Get two integers from the user


std::cout << "Please enter two integers to divide:";
std::cin >> dividend >> divisor;
// If possible, divide them and report the result
if (divisor != 0) {
quotient = dividend / divisor;
std::cout << dividend << " divided by " << divisor << " is "
<< quotient << '\n';
}
}

The assignment statement and printing statement are both a part of the body of the if statement. Given
the truth value of the Boolean expression divisor != 0 during a particular program run, either both
statements will be executed or neither statement will be executed.
A compound statement consists of zero or more statements grouped within curly braces. We say the
curly braces define a block of statements. As a matter of style many programmers always use curly braces
to delimit the body of an if statement even if the body contains only one statement:
if (x < 10) {
y = x;
}

They do this because it is easy to introduce a logic error if additional statements are added to the body later
and the programmer forgets to add then required curly braces.

92
Fundamentals of C++ Programming
5.5. THE IF/ELSE STATEMENT 93

The format of the following code


if (x < 10)
y = x;
z = x + 5;
implies that both assignments are part of the body of the if statement. Since
multiple statements making up the body must be in a compound statement within
curly braces, the compiler interprets the code fragment as if it had been written
if (x < 10)
y = x;
z = x + 5;
Such code will optionally execute the first assignment statement and always exe-
cute the second assignment statement.

The programmer probably meant to write it as


if (x < 10) {
y = x;
z = x + 5;
}

The curly braces are optional if the body consists of a single statement. If the body consists of only one
statement and curly braces are not used, then the semicolon that terminates the statement in the body also
terminates the if statement. If curly braces are used to delimit the body, a semicolon is not required after
the body’s close curly brace.
An empty pair of curly braces represents an empty block. An empty block is a valid compound state-
ment.

5.5 The if/else Statement

One undesirable aspect of Listing 5.3 (betterdivision.cpp) is if the user enters a zero divisor, the program
prints nothing. It may be better to provide some feedback to the user to indicate that the divisor provided
cannot be used. The if statement has an optional else clause that is executed only if the Boolean expres-
sion is false. Listing 5.5 (betterfeedback.cpp) uses the if/else statement to provide the desired effect.

Listing 5.5: betterfeedback.cpp


#include <iostream>

int main() {
int dividend, divisor;

// Get two integers from the user


std::cout << "Please enter two integers to divide:";
std::cin >> dividend >> divisor;
// If possible, divide them and report the result
93
Fundamentals of C++ Programming
5.5. THE IF/ELSE STATEMENT 94

Figure 5.2 if/else flowchart

yes Is no
divisor ≠ 0?

do the division
castigate user
and print result

if (divisor != 0)
std::cout << dividend << "/" << divisor << " = "
<< dividend/divisor << '\n';
else
std::cout << "Division by zero is not allowed\n";
}

A given program run will execute exactly one of either the if body or the else body. Unlike in
Listing 5.3 (betterdivision.cpp), a message is always displayed.

Please enter two integers to divide: 32 0


Division by zero is not allowed

The else clause contains an alternate body that is executed if the condition is false. The program’s flow of
execution is shown in Figure 5.2.
Listing 5.5 (betterfeedback.cpp) avoids the division by zero run-time error that causes the program to
terminate prematurely, but it still alerts the user that there is a problem. Another application may handle
the situation in a different way; for example, it may substitute some default value for divisor instead of
zero.
The general form of an if/else statement is
94
Fundamentals of C++ Programming
5.5. THE IF/ELSE STATEMENT 95

if ( condition )

statement 1

else

statement 2

• The reserved word if begins the if/else statement.


• The condition is a Boolean expression that determines whether the running program will execute
statement 1 or statement 2. As with the simple if statement, the condition must appear within paren-
theses.
• The program executes statement 1 if the condition is true. To make the if/else statement more
readable, indent statement 1 more spaces than the if line. This part of the if statement is sometimes
called the body of the if.
• The reserved word else begins the second part of the if/else statement.
• The program executes statement 2 if the condition is false. To make the if/else statement more
readable, indent statement 2 more spaces than the else line. This part of the if/else statement is
sometimes called the body of the else.

The body of the else clause of an if/else statement may be a compound statement:
if (x == y)
std::cout << x;
else {
x = 0;
std::cout << y;
}

or the if body alone may be a compound statement:


if (x == y) {
std::cout << x;
x = 0;
}
else
std::cout << y;

or both parts may be compound:


if (x == y) {
std::cout << x;
x = 0;
}
95
Fundamentals of C++ Programming
5.5. THE IF/ELSE STATEMENT 96

else {
std::cout << y;
y = 0;
}

or, as in Listing 5.5 (betterfeedback.cpp), both the if body and the else body can be simple statements.

Remember, if you wish to associate more than one statement with the body of
the if or else, you must use a compound statement. Compound statements are
enclosed within curly braces ({}).

If you ever attempt to use an if/else statement and discover that you need to leave the else clause
empty, as in
if (x == 2)
std::cout << "x = " << x << '\n';
else
; // Nothing to do otherwise

or, using a slightly different syntax, as


if (x == 2)
std::cout << "x = " << x << '\n';
else {
} // Nothing to do otherwise

you instead should use a simple if statement:


if (x == 2)
std::cout << "x = " << x << '\n';

The empty else clauses shown above do work, but they complicate the code and make it more difficult for
humans to read.
Due to the imprecise representation of floating-point numbers (see Listing 4.17 (imprecise5th.cpp) in
Section 4.1), programmers must use caution when using the equality operator (==) by itself to compare
floating-point expressions. Listing 5.6 (samedifferent.cpp) uses an if/else statement to demonstrate the
perils of using the equality operator with floating-point quantities.

Listing 5.6: samedifferent.cpp


#include <iostream>
#include <iomanip>

int main() {
double d1 = 1.11 - 1.10,
d2 = 2.11 - 2.10;
std::cout << "d1 = " << d1 << '\n';
std::cout << "d2 = " << d2 << '\n';
if (d1 == d2)
96
Fundamentals of C++ Programming
5.6. COMPOUND BOOLEAN EXPRESSIONS 97

std::cout << "Same\n";


else
std::cout << "Different\n";
std::cout << "d1 = " << std::setprecision(20) << d1 << '\n';
std::cout << "d2 = " << std::setprecision(20) << d2 << '\n';
}

In Listing 5.6 (samedifferent.cpp) the displayed values of d1 and d2 are rounded so they appear equivalent,
but internally the exact representations are slightly different. By including the header iomanip we can use
the std::setprecision stream manipulator to force std::cout to display more decimal places in
the floating-point number it prints. Observe from the output of Listing 5.6 (samedifferent.cpp) that the two
quantities that should be identically 0.01 are actually slightly different.

d1 = 0.01
d2 = 0.01
Different
d1 = 0.010000000000000008882
d2 = 0.0099999999999997868372

This result should not discourage you from using floating-point numbers where they truly are needed.
In Section 9.4.6 we will see how to handle floating-point comparisons properly.

5.6 Compound Boolean Expressions

Simple Boolean expressions, each involving one relational operator, can be combined into more complex
Boolean expressions using the logical operators && (and), || (or), and ! (not). A combination of two or
more Boolean expressions using logical operators is called a compound Boolean expression.
To introduce compound Boolean expressions, consider a computer science degree that requires, among
other computing courses, Operating Systems and Programming Languages. If we isolate those two courses,
we can say a student must successfully complete both Operating Systems and Programming Languages to
qualify for the degree. A student that passes Operating Systems but not Programming Languages will not
have met the requirements. Similarly, Programming Languages without Operating Systems is insufficient,
and a student completing neither Operating Systems nor Programming Languages surely does not qualify.
Logical AND works in exactly the same way. If e1 and e2 are two Boolean expressions, e1 && e2 is true
only if e1 and e2 are both true; if either one is false or both are false, the compound expression is false.
To illustrate logical OR, consider two mathematics courses, Differential Equations and Linear Algebra.
A computer science degree requires one of those two courses. A student who successfully completes Dif-
ferential Equations but does not take Linear Algebra meets the requirement. Similarly, a student may take
Linear Algebra but not Differential Equations. It is important to note the a student may elect to take both
Differential Equations and Linear Algebra (perhaps on the way to a mathematics minor), but the require-
ment is no less fulfilled.
Logical OR works in a similar fashion. Given our Boolean expressions e1 and e2 , the compound expres-
sion e1 || e2 is false only if e1 and e2 are both false; if either one is true or both are true, the compound
expression is true. Note that logical OR is an inclusive or, not an exclusive or. In informal conversion we
often imply exclusive or in a statement like “Would you like cake or ice cream for dessert?” The implication
is one or the other, not both. In computer programming the or is inclusive; if both subexpressions in an or
expression are true, the or expression is true.
97
Fundamentals of C++ Programming
5.6. COMPOUND BOOLEAN EXPRESSIONS 98

e1 e2 e1 && e2 e1 || e2 !e1
false false false false true
false true false true true
true false false true false
true true true true false

Table 5.3: Logical operators—e1 and e2 are Boolean expressions

Logical NOT simply reverses the truth value of the expression to which it is applied. If e is a true
Boolean expression, !e is false; if e is false, !e is true.
Table 5.3 is called a truth table. It shows all the combinations of truth values for two simple expressions
and the values of compound Boolean expressions built from applying the &&, ||, and ! C++ logical
operators.
Both && and || are binary operators; that is, they require two operands, both of which must be Boolean
expressions. Logical not (!) is a unary operator (see Section 4.1); it requires a single Boolean operand
immediately to its right.
Operator ! has higher precedence than both && and ||. && has higher precedence than ||. && and ||
are left associative; ! is right associative. && and || have lower precedence than any other binary operator
except assignment. This means the expression

x <= y && x <= z

is evaluated

(x <= y) && (x <= z)

Some programmers prefer to use the parentheses as shown here even though they are not required. The
parentheses improve the readability of complex expressions, and the compiled code is no less efficient.

98
Fundamentals of C++ Programming
5.6. COMPOUND BOOLEAN EXPRESSIONS 99

The relational operators such as < compare two operands. The result of the com-
parison is a Boolean value, which is freely convertible to an integer. The misappli-
cation of relational operators can lead to surprising results; consider, for example,
the expression
1 <= x <= 10
This expression is always true, regardless of the value of x! If the programmer’s
intent is to represent the mathematical notion of x falling within the range 1...10
inclusive, as in 1 ≤ x ≤ 10, the above C++ expression is not equivalent.

The expression
1 <= x <= 10
is evaluated as
(1 <= x) <= 10
If x is greater than or equal to one, the subexpression 1 <= x evaluates to true,
or integer 1. Integer 1, however, is always less than 10, so the overall expression
is true. If instead x is less than one, the subexpression 1 <= x evaluates to false,
or integer 0. Integer 0 is always less than 10, so the overall expression is true. The
problem is due to the fact that C++ does not strictly distinguish between Boolean
and integer values.

A correct way to represent the mathematical notion of 1 ≤ x ≤ 10 is


1 <= x && x <= 10
In this case x must simultaneously be greater than or equal to 1 and less than
or equal to 10. The revised Boolean expression is a little more verbose than the
mathematical representation, but it is the correct formulation for C++.

The following section of code assigns the indicated values to a bool:


bool b;
int x = 10;
int y = 20;
b = (x == 10); // assigns true to b
b = (x != 10); // assigns false to b
b = (x == 10 && y == 20); // assigns true to b
b = (x != 10 && y == 20); // assigns false to b
b = (x == 10 && y != 20); // assigns false to b
b = (x != 10 && y != 20); // assigns false to b
b = (x == 10 || y == 20); // assigns true to b
b = (x != 10 || y == 20); // assigns true to b
b = (x == 10 || y != 20); // assigns true to b
b = (x != 10 || y != 20); // assigns false to b

Convince yourself that the following expressions are equivalent:


(x != y)
99
Fundamentals of C++ Programming
5.6. COMPOUND BOOLEAN EXPRESSIONS 100

!(x == y)
(x < y || x > y)

In the expression e1 && e2 both subexpressions e1 and e2 must be true for the overall expression to be
true. Since the && operator evaluates left to right, this means that if e1 is false, there is no need to evaluate
e2 . If e1 is false, no value of e2 can make the expression e1 && e2 true. The logical and operator first tests the
expression to its left. If it finds the expression to be false, it does not bother to check the right expression.
This approach is called short-circuit evaluation. In a similar fashion, in the expression e1 || e2 , if e1 is
true, then it does not matter what value e2 has—a logical or expression is true unless both subexpressions
are false. The || operator uses short-circuit evaluation also.
Why is short-circuit evaluation important? Two situations show why it is important to consider:

• The order of the subexpressions can affect performance. When a program is running, complex ex-
pressions require more time for the computer to evaluate than simpler expressions. We classify an
expression that takes a relatively long time to evaluate as an expensive expression. If a compound
Boolean expression is made up of an expensive Boolean subexpression and an less expensive Boolean
subexpression, and the order of evaluation of the two expressions does not affect the behavior of the
program, then place the more expensive Boolean expression second. If the first subexpression is false
and && is being used, then the expensive second subexpression is not evaluated; if the first subex-
pression is true and || is being used, then, again, the expensive second subexpression is avoided.

• Subexpressions can be ordered to prevent run-time errors. This is especially true when one of the
subexpressions depends on the other in some way. Consider the following expression:

(x != 0) && (z/x > 1)

Here, if x is zero, the division by zero is avoided. If the subexpressions were switched, a run-time
error would result if x is zero.

100
Fundamentals of C++ Programming
5.7. NESTED CONDITIONALS 101

Arity Operators Associativity


unary (post) ++, (post) --, static_cast
unary (pre) ++, (pre) --, !, +, -
binary *, /, % left
binary +, - left
binary <<, >> left
binary >, <, >=, <= left
binary ==, != left
binary && left
binary || left
binary =, +=, -=, *=, /=, %= right

Table 5.4: Precedence of C++ Operators (High to Low)

Suppose you wish to print the word “OK” if a variable x is 1, 2, or 3. An informal


translation from English might yield:
if (x == 1 || 2 || 3)
std::cout << "OK\n";
Unfortunately, x’s value is irrelevant; the code always prints the word “OK.” Since
the == operator has higher precedence than ||, the expression
x == 1 || 2 || 3
is interpreted as
(x == 1) || 2 || 3
The expression x == 1 is either true or false, but integer 2 is always interpreted
as true, and integer 3 is interpreted as true is as well.

The correct statement would be


if (x == 1 || x == 2 || x == 3)
std::cout << "OK\n";
The revised Boolean expression is more verbose and less similar to the English
rendition, but it is the correct formulation for C++.

Our current list of C++ operators is shown in Table 5.4.

5.7 Nested Conditionals

The statements in the body of the if or the else may be any C++ statements, including other if/else
statements. We can use nested if statements to build arbitrarily complex control flow logic. Consider
Listing 5.7 (checkrange.cpp) that determines if a number is between 0 and 10, inclusive.

Listing 5.7: checkrange.cpp


#include <iostream>

101
Fundamentals of C++ Programming
5.7. NESTED CONDITIONALS 102

int main() {
int value;
std::cout << "Please enter an integer value in the range 0...10: ";
std::cin >> value;
if (value >= 0) // First check
if (value <= 10) // Second check
std::cout << "In range";
std::cout << "Done\n";
}

Listing 5.7 (checkrange.cpp) behaves as follows:

• The program checks the value >= 0 condition first. If value is less than zero, the executing
program does not evaluate the second condition and does not print In range, but it immediately
executes the print statement following the outer if statement which prints Done.

• If the executing program finds value to be greater than or equal to zero, it checks the second condi-
tion. If the second condition is met, it displays the In range message; otherwise, it is not. Regardless,
the program prints Done before it terminates.

For the program to display the message In range both conditions of this nested if must be met. Said
another way, the first condition and the second condition must be met for the In range message to be printed.
From this perspective, we can rewrite the program to behave the same way with only one if statement, as
Listing 5.8 (newcheckrange.cpp) shows.

Listing 5.8: newcheckrange.cpp


#include <iostream>

int main() {
int value;
std::cout << "Please enter an integer value in the range 0...10: ";
std::cin >> value;
if (value >= 0 && value <= 10)
std::cout << "In range\n";
}

Listing 5.8 (newcheckrange.cpp) uses a logical && to check both conditions at the same time. Its logic
is simpler, using only one if statement, at the expense of a slightly more complex Boolean expression in
its condition. The second version is preferable here because simpler logic is usually a desirable goal.
Sometimes a program’s logic cannot be simplified as in Listing 5.8 (newcheckrange.cpp). In Listing 5.9
(enhancedcheckrange.cpp) one if statement alone is insufficient to implement the necessary behavior.

Listing 5.9: enhancedcheckrange.cpp


#include <iostream>

int main() {
int value;
std::cout << "Please enter an integer value in the range 0...10: ";
std::cin >> value;
if (value >= 0) // First check
if (value <= 10) // Second check
102
Fundamentals of C++ Programming
5.7. NESTED CONDITIONALS 103

std::cout << value << " is acceptable\n";


else
std::cout << value << " is too large\n";
else
std::cout << value << " is too small\n";
}

Listing 5.9 (enhancedcheckrange.cpp) provides a more specific message instead of a simple notification
of acceptance. The program prints exactly one of three messages based on the value of the variable. A single
if or if/else statement cannot choose from among more than two different execution paths.
Listing 5.10 (binaryconversion.cpp) uses a series of if statements to print a 10-bit binary string repre-
senting the binary equivalent of a decimal integer supplied by the user. (Section 4.8 provides some back-
ground information about the binary number system.) We use if/else statements to print the individual
digits left to right, essentially assembling the sequence of bits that represents the binary number.

Listing 5.10: binaryconversion.cpp


#include <iostream>

int main() {
int value;
// Get number from the user
std::cout << "Please enter an integer value in the range 0...1023: ";
std::cin >> value;
// Integer must be less than 1024
if (0 <= value && value < 1024) {
if (value >= 512) {
std::cout << 1;
value %= 512;
}
else
std::cout << 0;
if (value >= 256) {
std::cout << 1;
value %= 256;
}
else
std::cout << 0;
if (value >= 128) {
std::cout << 1;
value %= 128;
}
else
std::cout << 0;
if (value >= 64) {
std::cout << 1;
value %= 64;
}
else
std::cout << 0;
if (value >= 32) {
std::cout << 1;
value %= 32;
} else
103
Fundamentals of C++ Programming
5.7. NESTED CONDITIONALS 104

std::cout << 0;
if (value >= 16) {
std::cout << 1;
value %= 16;
}
else
std::cout << 0;
if (value >= 8) {
std::cout << 1;
value %= 8;
}
else
std::cout << 0;
if (value >= 4) {
std::cout << 1;
value %= 4;
}
else
std::cout << 0;
if (value >= 2) {
std::cout << 1;
value %= 2;
}
else
std::cout << 0;
std::cout << value << '\n';
}
}

In Listing 5.10 (binaryconversion.cpp):

• The outer if checks to see if the value the use provides is in the proper range. The program works
only for nonnegative integer values less than 1,024, so the range is 0-1023.
• Each inner if compares the user-supplied entered integer against decreasing powers of two. If the
number is large enough, the program:
– prints the digit 1 to the console, and
– removes via the remainder operator that power of two’s contribution to the value.
If the number is not at least as big as the given power of two, the program prints a 0 instead and
moves on without modifying the input value.
• For the ones place at the end no check is necessary—the remaining value will be 0 or 1 and so the
program prints whatever remains.

The following shows a sample run of Listing 5.10 (binaryconversion.cpp):


Please enter an integer value in the range 0...1023: 805
1100100101

Figure 5.3 illustrates the execution of Listing 5.10 (binaryconversion.cpp) when the user enters 805.
Listing 5.11 (simplerbinaryconversion.cpp) simplifies the logic of Listing 5.10 (binaryconversion.cpp)
at the expense of some additional arithmetic. It uses only one if statement.
104
Fundamentals of C++ Programming
5.7. NESTED CONDITIONALS 105

Figure 5.3 The process of the binary number conversion program when the user supplies 805 as the input
value.

Print prompt Remainder 293÷256 → 37


37 ≥ 128? 5 ≥ 4?
→ Please enter ...
Yes, → 1
No, → 0

Get value
37 ≥ 64? Remainder 5÷4 → 1
← 805 1 ≥ 2?
No, → 0
No, → 0
0 ≤ 805 ≤ 1023? 37 ≥ 32?

Yes Print remaining value


Yes, → 1
→ 1

805 ≥ 512? Remainder 37÷32 → 5


5 ≥ 16?
Yes, → 1
No, → 0
Remainder 805÷512 → 293
293 ≥ 256? 5 ≥ 8?

Yes, → 1 No, → 0

105
Fundamentals of C++ Programming
5.7. NESTED CONDITIONALS 106

Listing 5.11: simplerbinaryconversion.cpp


#include <iostream>

int main() {
int value;
// Get number from the user
std::cout << "Please enter an integer value in the range 0...1023: ";
std::cin >> value;
// Integer must be less than 1024
if (0 <= value && value < 1024) {
std::cout << value/512;
value %= 512;
std::cout << value/256;
value %= 256;
std::cout << value/128;
value %= 128;
std::cout << value/64;
value %= 64;
std::cout << value/32;
value %= 32;
std::cout << value/16;
value %= 16;
std::cout << value/8;
value %= 8;
std::cout << value/4;
value %= 4;
std::cout << value/2;
value %= 2;
std::cout << value << '\n';
}
}

The sole if statement in Listing 5.11 (simplerbinaryconversion.cpp) ensures that the user provides an inte-
ger in the proper range. The other if statements that originally appeared in Listing 5.10 (binaryconversion.cpp)
are gone. A clever sequence of integer arithmetic operations replace the original conditional logic. The two
programs—binaryconversion.cpp and simplerbinaryconversion.cpp—behave identically but simplerbinarycon-
version.cpp’s logic is simpler.
Listing 5.12 (troubleshoot.cpp) implements a very simple troubleshooting program that (an equally
simple) computer technician might use to diagnose an ailing computer.

Listing 5.12: troubleshoot.cpp


#include <iostream>

int main() {
std::cout << "Help! My computer doesn't work!\n";
char choice;
std::cout << "Does the computer make any sounds "
<< "(fans, etc.) or show any lights? (y/n):";
std::cin >> choice;
// The troubleshooting control logic
if (choice == 'n') { // The computer does not have power
std::cout << "Is it plugged in? (y/n):";
std::cin >> choice;
106
Fundamentals of C++ Programming
5.7. NESTED CONDITIONALS 107

if (choice == 'n') { // It is not plugged in, plug it in


std::cout << "Plug it in. If the problem persists, "
<< "please run this program again.\n";
}
else { // It is plugged in
std::cout << "Is the switch in the \"on\" position? (y/n):";
std::cin >> choice;
if (choice == 'n') { // The switch is off, turn it on!
std::cout << "Turn it on. If the problem persists, "
<< "please run this program again.\n";
}
else { // The switch is on
std::cout << "Does the computer have a fuse? (y/n):";
std::cin >> choice;
if (choice == 'n') { // No fuse
std::cout << "Is the outlet OK? (y/n):";
std::cin >> choice;
if (choice == 'n') { // Fix outlet
std::cout << "Check the outlet's circuit "
<< "breaker or fuse. Move to a "
<< "new outlet, if necessary. "
<< "If the problem persists, "
<< "please run this program again.\n";
}
else { // Beats me!
std::cout << "Please consult a service "
<< "technician.\n";
}
}
else { // Check fuse
std::cout << "Check the fuse. Replace if "
<< "necessary. If the problem "
<< "persists, then "
<< "please run this program again.\n";
}
}
}
}
else { // The computer has power
std::cout << "Please consult a service technician.\n";
}
}

This very simple troubleshooting program attempts to diagnose why a computer does not work. The
potential for enhancement is unlimited, but this version only deals with power issues that have simple fixes.
Notice that if the computer has power (fan or disk drive makes sounds or lights are visible), the program
directs the user to seek help elsewhere! The decision tree capturing the basic logic of the program is shown
in Figure 5.4.
The steps performed are:

1. Is it plugged in? This simple fix is sometimes overlooked.

2. Is the switch in the on position? This is another simple fix.


107
Fundamentals of C++ Programming
5.7. NESTED CONDITIONALS 108

Figure 5.4 Decision tree for troubleshooting a computer system

3. If applicable, is the fuse blown? Some computer systems have a user-serviceable fuse that can blow
out during a power surge. (Most newer computers have power supplies that can handle power surges
and have no user-serviceable fuses.)

4. Is there power at the receptacle? Perhaps the outlet’s circuit breaker or fuse has a problem.

The program directs the user to make the easier checks first. It progressively introduces more difficult
checks as it continues. Based on your experience with troubleshooting computers that do not run properly,
you may be able to think of many enhancements to this simple program.
Note that in Listing 5.12 (troubleshoot.cpp) curly braces are used in many places where they strictly are
not necessary. Their inclusion in Listing 5.12 (troubleshoot.cpp) improves the readability of the program and
makes the logic easier to understand. Even if you do not subscribe to the philosophy of using curly braces
for every if/else body, it is a good idea to use them in situations that improve the code’s readability.
Recall the time conversion program in Listing 4.11 (timeconv.cpp). If the user enters 10000, the pro-
gram runs as follows:

Please enter the number of seconds:10000


2 hr 46 min 40 sec

and if the user enters 9961, the program prints:

Please enter the number of seconds:9961


2 hr 46 min 1 sec

108
Fundamentals of C++ Programming
5.7. NESTED CONDITIONALS 109

Suppose we wish to improve the English presentation by not using abbreviations. If we spell out hours,
minutes, and seconds, we must be careful to use the singular form hour, minute, or second when the corre-
sponding value is one. Listing 5.13 (timeconvcond1.cpp) uses if/else statements to express to time units
with the correct number.

Listing 5.13: timeconvcond1.cpp


// File timeconvcond1.cpp

#include <iostream>

int main() {
// Some useful conversion constants
const int SECONDS_PER_MINUTE = 60,
SECONDS_PER_HOUR = 60*SECONDS_PER_MINUTE; // 3600
int hours, minutes, seconds;
std::cout << "Please enter the number of seconds:";
std::cin >> seconds;
// First, compute the number of hours in the given number
// of seconds
hours = seconds / SECONDS_PER_HOUR; // 3600 seconds = 1 hours
// Compute the remaining seconds after the hours are
// accounted for
seconds = seconds % SECONDS_PER_HOUR;
// Next, compute the number of minutes in the remaining
// number of seconds
minutes = seconds / SECONDS_PER_MINUTE; // 60 seconds = 1 minute
// Compute the remaining seconds after the minutes are
// accounted for
seconds = seconds % SECONDS_PER_MINUTE;
// Report the results
std::cout << hours;
// Decide between singular and plural form of hours
if (hours == 1)
std::cout << " hour ";
else
std::cout << " hours ";
std::cout << minutes;
// Decide between singular and plural form of minutes
if (minutes == 1)
std::cout << " minute ";
else
std::cout << " minutes ";
std::cout << seconds;
// Decide between singular and plural form of seconds
if (seconds == 1)
std::cout << " second";
else
std::cout << " seconds";
std::cout << '\n';
}

The if/else statements within Listing 5.13 (timeconvcond1.cpp) are responsible for printing the correct
version—singular or plural—for each time unit. One run of Listing 5.13 (timeconvcond1.cpp) produces

109
Fundamentals of C++ Programming
5.7. NESTED CONDITIONALS 110

Please enter the number of seconds:10000


2 hours 46 minutes 40 seconds

All the words are plural since all the value are greater than one. Another run produces
Please enter the number of seconds:9961
2 hours 46 minutes 1 second

Note the word second is singular as it should be.


Please enter the number of seconds:3601
1 hour 0 minutes 1 second

Here again the printed words agree with the number of the value they represent.
An improvement to Listing 5.13 (timeconvcond1.cpp) would not print a value and its associated time
unit if the value is zero. Listing 5.14 (timeconvcond2.cpp) adds this feature.

Listing 5.14: timeconvcond2.cpp


// File timeconvcond1.cpp

#include <iostream>

int main() {
// Some useful conversion constants
const int SECONDS_PER_MINUTE = 60,
SECONDS_PER_HOUR = 60*SECONDS_PER_MINUTE; // 3600
int hours, minutes, seconds;
std::cout << "Please enter the number of seconds:";
std::cin >> seconds;
// First, compute the number of hours in the given number
// of seconds
hours = seconds / SECONDS_PER_HOUR; // 3600 seconds = 1 hours
// Compute the remaining seconds after the hours are
// accounted for
seconds = seconds % SECONDS_PER_HOUR;
// Next, compute the number of minutes in the remaining
// number of seconds
minutes = seconds / SECONDS_PER_MINUTE; // 60 seconds = 1 minute
// Compute the remaining seconds after the minutes are
// accounted for
seconds = seconds % SECONDS_PER_MINUTE;
// Report the results
if (hours > 0) { // Print hours at all?
std::cout << hours;
// Decide between singular and plural form of hours
if (hours == 1)
std::cout << " hour ";
else
std::cout << " hours ";
}
if (minutes > 0) { // Print minutes at all?
std::cout << minutes;
// Decide between singular and plural form of minutes
110
Fundamentals of C++ Programming
5.7. NESTED CONDITIONALS 111

if (minutes == 1)
std::cout << " minute ";
else
std::cout << " minutes ";
}
// Print seconds at all?
if (seconds > 0 || (hours == 0 && minutes == 0 && seconds == 0)) {
std::cout << seconds;
// Decide between singular and plural form of seconds
if (seconds == 1)
std::cout << " second";
else
std::cout << " seconds";
}
std::cout << '\n';
}

In Listing 5.14 (timeconvcond2.cpp) each code segment responsible for printing a time value and its English
word unit is protected by an if statement that only allows the code to execute if the time value is greater
than zero. The exception is in the processing of seconds: if all time values are zero, the program should print
0 seconds. Note that each of the if/else statements responsible for determining the singular or plural
form is nested within the if statement that determines whether or not the value will be printed at all.
One run of Listing 5.14 (timeconvcond2.cpp) produces
Please enter the number of seconds:10000
2 hours 46 minutes 40 seconds

All the words are plural since all the value are greater than one. Another run produces
Please enter the number of seconds:9961
2 hours 46 minutes 1 second

Note the word second is singular as it should be.


Please enter the number of seconds:3601
1 hour 1 second

Here again the printed words agree with the number of the value they represent.
Please enter the number of seconds:7200
2 hours

Another run produces:


Please enter the number of seconds:60
1 minute

Finally, the following run shows that the program handles zero seconds properly:
Please enter the number of seconds:0
0 seconds

111
Fundamentals of C++ Programming
5.8. MULTI-WAY IF/ELSE STATEMENTS 112

Figure 5.5 Flowchart with multiple optional execution pathways

5.8 Multi-way if/else Statements

A simple if/else statement can select from between two execution paths. Suppose we wish to choose
one execution path from among several possible paths, as shown in Figure 5.5?
Listing 5.9 (enhancedcheckrange.cpp) showed how to select from among three options. What if exactly
one of many actions should be taken? Nested if/else statements are required, and the form of these
nested if/else statements is shown in Listing 5.15 (digittoword.cpp).

Listing 5.15: digittoword.cpp


#include <iostream>

int main() {
int value;
std::cout << "Please enter an integer in the range 0...5: ";
std::cin >> value;
if (value < 0)
std::cout << "Too small";
else
if (value == 0)
std::cout << "zero";
else
if (value == 1)
std::cout << "one";
else
if (value == 2)
std::cout << "two";
else
if (value == 3)
std::cout << "three";
else
112
Fundamentals of C++ Programming
5.8. MULTI-WAY IF/ELSE STATEMENTS 113

if (value == 4)
std::cout << "four";
else
if (value == 5)
std::cout << "five";
else
std::cout << "Too large";
std::cout << '\n';
}

Observe the following about Listing 5.15 (digittoword.cpp):

• It prints exactly one of eight messages depending on the user’s input.


• Notice that each if body contains a single printing statement and each else body, except the last
one, contains an if statement. The control logic forces the program execution to check each condition
in turn. The first condition that matches wins, and its corresponding if body will be executed. If none
of the conditions are true, the last else’s Too large message will be printed.
• No curly braces are necessary to delimit the if or else bodies since each body contains only a
single statement (although a single deeply nested if/else statement is a mighty big statement).

Listing 5.15 (digittoword.cpp) is formatted according to the conventions used in earlier examples. As
a consequence, the mass of text drifts to the right as more conditions are checked. A commonly used
alternative style, shown in Listing 5.16 (restyleddigittoword.cpp), avoids this rightward drift.

Listing 5.16: restyleddigittoword.cpp


#include <iostream>

int main() {
int value;
std::cout << "Please enter an integer in the range 0...5: ";
std::cin >> value;
if (value < 0)
std::cout << "Too small";
else if (value == 0)
std::cout << "zero";
else if (value == 1)
std::cout << "one";
else if (value == 2)
std::cout << "two";
else if (value == 3)
std::cout << "three";
else if (value == 4)
std::cout << "four";
else if (value == 5)
std::cout << "five";
else
std::cout << "Too large";
std::cout << '\n';
}

Based on our experience so far, the formatting of Listing 5.16 (restyleddigittoword.cpp) somewhat hides
the true structure of the program’s logic, but this style of formatting multi-way if/else statements is
113
Fundamentals of C++ Programming
5.8. MULTI-WAY IF/ELSE STATEMENTS 114

so common that it is regarded as acceptable by most programmers. The sequence of else if lines all
indented to the same level identifies this construct as a multi-way if/else statement.
Listing 5.17 (datetransformer.cpp) uses a multi-way if/else to transform a numeric date in month/-
day format to an expanded US English form and an international Spanish form; for example, 2/14 would
be converted to February 14 and 14 febrero.

Listing 5.17: datetransformer.cpp


#include <iostream>

int main() {
std::cout << "Please enter the month and day as numbers: ";
int month, day;
std::cin >> month >> day;
// Translate month into English
if (month == 1)
std::cout << "January";
else if (month == 2)
std::cout << "February";
else if (month == 3)
std::cout << "March";
else if (month == 4)
std::cout << "April";
else if (month == 5)
std::cout << "May";
else if (month == 6)
std::cout << "June";
else if (month == 7)
std::cout << "July";
else if (month == 8)
std::cout << "August";
else if (month == 9)
std::cout << "September";
else if (month == 10)
std::cout << "October";
else if (month == 11)
std::cout << "November";
else
std::cout << "December";
// Add the day
std::cout << " " << day << " or " << day << " de ";
// Translate month into Spanish
if (month == 1)
std::cout << "enero";
else if (month == 2)
std::cout << "febrero";
else if (month == 3)
std::cout << "marzo";
else if (month == 4)
std::cout << "abril";
else if (month == 5)
std::cout << "mayo";
else if (month == 6)
std::cout << "junio";
else if (month == 7)
114
Fundamentals of C++ Programming
5.8. MULTI-WAY IF/ELSE STATEMENTS 115

std::cout << "julio";


else if (month == 8)
std::cout << "agosto";
else if (month == 9)
std::cout << "septiembre";
else if (month == 10)
std::cout << "octubre";
else if (month == 11)
std::cout << "noviembre";
else
std::cout << "diciembre";
std::cout << '\n';
}

A sample run of Listing 5.17 (datetransformer.cpp) is shown here:


Please enter the month and day as numbers: 5 20
May 20 or 20 de mayo

Figure 5.6 compares the structure of the if/else statements in a program such as Listing 5.16
(restyleddigittoword.cpp) to those in a program like Listing 5.10 (binaryconversion.cpp).
In a program like Listing 5.16 (restyleddigittoword.cpp), the if/else statements are nested, while in
a program like Listing 5.10 (binaryconversion.cpp) the if/else statements are sequential.
C++ provides the tools to construct some very complicated conditional statements. It is important to
resist the urge to make things overly complex. Consider the problem of computing the maximum of five
integer values provided by the user. The complete solution is left as an exercise in Section 5.10, but here we
will outline an appropriate strategy.
Suppose you allow the user to enter all the values at once; for example, for integer variables n1, n2,
n3, n4, and n5:
std::cout << "Please enter five integer values: ";
std::cin >> n1 >> n2 >> n3 >> n4 >> n5;

Now, allow yourself one extra variable called max. All variables have a meaning, and their names
should reflect their meaning in some way. We’ll let our additional max variable mean "maximum I have
determined so far." The following is one approach to the solution:

1. Set max equal to n1. This means as far as we know at the moment, n1 is the biggest number because
max and n1 have the same value.
2. Compare max to n2. If n2 is larger than max, change max to have n2’s value to reflect the fact that
we determined n2 is larger; if n2 is not larger than max, we have no reason to change max, so do
not change it.
3. Compare max to n3. If n3 is larger than max, change max to have n3’s value to reflect the fact that
we determined n3 is larger; if n3 is not larger than max, we have no reason to change max, so do
not change it.
4. Follow the same process for n4 and n5.

In the end the meaning of the max variable remains the same–"maximum I have determined so far," but,
after comparing max to all the input variables, we now know that it is the maximum value of all five input
115
Fundamentals of C++ Programming
5.8. MULTI-WAY IF/ELSE STATEMENTS 116

Figure 5.6 The structure of the if statements in a program such as Listing 5.16 (restyleddigittoword.cpp)
(left) versus those in a program like Listing 5.10 (binaryconversion.cpp) (right)

116
Fundamentals of C++ Programming
5.9. ERRORS IN CONDITIONAL STATEMENTS 117

numbers. The extra variable max is not strictly necessary, but it makes thinking about the problem and its
solution easier.
Something to think about: Do you want a series of if statements or one large multiway if/else
construct?
Also, you may be tempted to write logic such as
if (n1 >= n2 && n1 >= n3 && n1 >= n4 && n1 >=n5)
std::cout << "The maximum is " << n1 << '\n';
else if ( n2 >= n1 && n2 >= n3 && // the rest omitted . . .

This will work, but this logic is much more complicated and less efficient (every >= and && operation
requires a few machine cycles to execute). Since it is more complicated, it is more difficult to write correctly,
in addition to being more code to type in. It is easy to use > by mistake instead of >=, which will not produce
the correct results. Also, if you use this more complicated logic and decide later to add more variables, you
will need to change all of the if conditions in your code and, of course, make sure to modify each one
of the conditions correctly. If you implement the simpler strategy outlined before, you need only add one
simple if statement for each additional variable.
Chapter 6 introduces loops, the ability to execute statements repeatedly. You easily can adapt the first
approach to allow the user to type in as many numbers as they like and then have the program report the
maximum number the user entered. The second approach with the more complex logic cannot be adapted
in this manner. With the first approach you end up with cleaner, simpler logic, a more efficient program,
and code that is easier to extend.

5.9 Errors in Conditional Statements

Consider Listing 5.18 (badequality.cpp).

Listing 5.18: badequality.cpp


#include <iostream>

int main() {
int input;
std::cout << "Please enter an integer:";
std::cin >> input;
if (input = 2)
std::cout << "two\n";
std::cout << "You entered " << input << '\n';
}

Listing 5.18 (badequality.cpp) demonstrates a common mistake—using the assignment operator where
the equality operator is intended. This program, when run, always prints the message “two” and insists the
user entered 2 regardless of the actual input. Recall from Section 4.3 that the assignment expression has a
value. The value of an assignment expression is same as the value that is assigned; thus, the expression
input = 2

has the value 2. When you consider also that every integer can be treated as a Boolean value (see Section 5.1)
and any non-zero value is interpreted as true, you can see that the condition of if statement
117
Fundamentals of C++ Programming
5.10. EXERCISES 118

if (input = 2)
std::cout << "two\n";

is always true. Additionally, the variable input is always assigned the value 2.
Since it is such a common coding error, most C++ compilers can check for such misuse of assignment.
At warning Level 4, for example, Visual C++ will issue a warning when assignment appears where a
conditional expression is expected:

warning C4706: assignment within conditional expression

Occasionally the use of assignment within a conditional expression is warranted, so the compiler does not
perform this check by default. For our purposes it is good idea to direct the compiler to perform this extra
check.
Carefully consider each compound conditional used, such as
value > 0 && value <= 10

found in Listing 5.8 (newcheckrange.cpp). Confusing logical and and logical or is a common programming
error. If you substitute || for &&, the expression
x > 0 || x <= 10

is always true, no matter what value is assigned to the variable x. A Boolean expression that is always true
is known as a tautology. Think about it. If x is an int, what value could the variable x assume that would
make
x > 0 || x <= 10

false? Regardless of its value, one or both of the subexpressions will be true, so this compound logical or
expression is always true. This particular or expression is just a complicated way of expressing the value
true.
Another common error is contriving compound Boolean expressions that are always false, known as
contradictions. Suppose you wish to exclude values from a given range; for example, reject values in the
range 0...10 and accept all other numbers. Is the Boolean expression in the following code fragment up to
the task?
// I want to use all but 0, 1, 2, ..., 10
if (value < 0 && value > 10)
/* Code to execute goes here . . . */

A closer look at the condition reveals it can never be true. What number can be both less than zero and
greater than ten at the same time? None can, of course, so the expression is a contradiction and a complicated
way of expressing false. To correct this code fragment, replace the && operator with ||.

5.10 Exercises

1. What values can a variable of type bool assume?

2. Where does the term bool originate?


118
Fundamentals of C++ Programming
5.10. EXERCISES 119

3. What is the integer equivalent to true in C++?


4. What is the integer equivalent to false in C++?
5. Is the value -16 interpreted as true or false?
6. May an integer value be assigned to a bool variable?
7. Can true be assigned to an int variable?
8. Given the following declarations:
int x = 3, y = 5, z = 7;
bool b1 = true, b2 = false, b3 = x == 3, b4 = y < 3;

evaluate the following Boolean expressions:

(a) x == 3
(b) x < y
(c) x >= y
(d) x <= y
(e) x != y - 2
(f) x < 10
(g) x >= 0 && x < 10
(h) x < 0 && x < 10
(i) x >= 0 && x < 2
(j) x < 0 || x < 10
(k) x > 0 || x < 10
(l) x < 0 || x > 10
(m) b1
(n) !b1
(o) !b2
(p) b1 && b2

9. Express the following Boolean expressions in simpler form; that is, use fewer operators. x is an int.
(a) !(x == 2)
(b) x < 2 || x == 2
(c) !(x < y)
(d) !(x <= y)
(e) x < 10 && x > 20
(f) x > 10 || x < 20
(g) x != 0
(h) x == 0
10. What is the simplest tautology?
11. What is the simplest contradiction?
119
Fundamentals of C++ Programming
5.10. EXERCISES 120

12. Write a C++ program that requests an integer value from the user. If the value is between 1 and 100
inclusive, print “OK;” otherwise, do not print anything.

13. Write a C++ program that requests an integer value from the user. If the value is between 1 and 100
inclusive, print “OK;” otherwise, print “Out of range.”

14. The following program attempts to print a message containing the English word corresponding to a
given integer input. For example, if the user enters the value 3, the program should print "You entered a three".
In its current state, the program contains logic errors. Locate the problems and repair them so the pro-
gram will work as expected.
#include <iostream>

int main() {
std::cout << "Please in value in the range 1...5: ";
int value;
std::cin >> value;
// Translate number into its English word
if (month == 1)
std::cout << "You entered a";
std::cout << "one";
std::cout << '\n';
else if (month == 2)
std::cout << "You entered a";
std::cout << "two";
std::cout << '\n';
else if (month == 3)
std::cout << "You entered a";
std::cout << "three";
std::cout << '\n';
else if (month == 4)
std::cout << "You entered a";
std::cout << "four";
std::cout << '\n';
else if (month == 5)
std::cout << "You entered a";
std::cout << "five";
std::cout << '\n';
else // Value out of range
std::cout << "You entered a";
std::cout << "value out of range";
std::cout << '\n';
}

15. Consider the following section of C++ code:


// i, j, and k are ints
if (i < j) {
if (j < k)
i = j;
else
j = k;
120
Fundamentals of C++ Programming
5.10. EXERCISES 121

}
else {
if (j > k)
j = i;
else
i = k;
}
std::cout << "i = " << i << " j = " << j << " k = " << k << '\n';

What will the code print if the variables i, j, and k have the following values?

(a) i is 3, j is 5, and k is 7
(b) i is 3, j is 7, and k is 5
(c) i is 5, j is 3, and k is 7
(d) i is 5, j is 7, and k is 3
(e) i is 7, j is 3, and k is 5
(f) i is 7, j is 5, and k is 3

16. Consider the following C++ program that prints one line of text:
#include <iostream>

int main() {
int input;
std::cin >> input;
if (input < 10) {
if (input != 5)
std::cout << "wow ";
else
input++;
}
else {
if (input == 17)
input += 10;
else
std::cout << "whoa ";
}
std::cout << input << '\n';
}

What will the program print if the user provides the following input?

(a) 3
(b) 21
(c) 5
(d) 17
(e) -5

17. Why does the following section of code always print "ByeHi"?
121
Fundamentals of C++ Programming
5.10. EXERCISES 122

int x;
std::cin >> x;
if (x < 0);
std::cout << "Bye";
std::cout << "Hi\n";

18. Write a C++ program that requests five integer values from the user. It then prints the maximum and
minimum values entered. If the user enters the values 3, 2, 5, 0, and 1, the program would indicate
that 5 is the maximum and 0 is the minimum. Your program should handle ties properly; for example,
if the user enters 2, 4, 2, 3, and 3, the program should report 2 as the minimum and 4 as maximum.
19. Write a C++ program that requests five integer values from the user. It then prints one of two
things: if any of the values entered are duplicates, it prints "DUPLICATES"; otherwise, it prints
"ALL UNIQUE".

122
Fundamentals of C++ Programming
123

Chapter 6

Iteration

Iteration repeats the execution of a sequence of code. Iteration is useful for solving many programming
problems. Iteration and conditional execution are key components of algorithm construction.

6.1 The while Statement

Listing 6.1 (counttofive.cpp) counts to five by printing a number on each output line.

Listing 6.1: counttofive.cpp


#include <iostream>

int main() {
std::cout << 1 << '\n';
std::cout << 2 << '\n';
std::cout << 3 << '\n';
std::cout << 4 << '\n';
std::cout << 5 << '\n';
}

When compiled and run, this program displays


1
2
3
4
5

How would you write the code to count to 10,000? Would you copy, paste, and modify 10,000 printing
statements? You could, but that would be impractical! Counting is such a common activity, and computers
routinely count up to very large values, so there must be a better way. What we really would like to do
is print the value of a variable (call it count), then increment the variable (count++), and repeat this
process until the variable is large enough (count == 5 or perhaps count == 10000). This process
of executing the same section of code over and over is known as iteration, or looping, and in C++ we can
implement loops in several different ways.
123
Fundamentals of C++ Programming
6.1. THE WHILE STATEMENT 124

Listing 6.2 (iterativecounttofive.cpp) uses a while statement to count to five:

Listing 6.2: iterativecounttofive.cpp


#include <iostream>

int main() {
int count = 1; // Initialize counter
while (count <= 5) {
std::cout << count << '\n'; // Display counter, then
count++; // Increment counter
}
}

Listing 6.2 (iterativecounttofive.cpp) uses a while statement to display a variable that is counting
up to five. Unlike the approach taken in Listing 6.1 (counttofive.cpp), it is trivial to modify Listing 6.2
(iterativecounttofive.cpp) to count up to 10,000—just change the literal value 5 to 10000.
The line
while (count <= 5)

begins the while statement. The expression within the parentheses must be a Boolean expression. If the
Boolean expression is true when the the program’s execution reaches the while statement, the program
executes the body of the while statement and then checks the condition again. The program repeatedly
executes the statement(s) within the body of the while as long as the Boolean expression remains true.
If the Boolean expression is true when the while statement is executed, the body of the while state-
ment is executed, and the body is executed repeatedly as long as the Boolean expression remains true.
The statements
std::cout << count << '\n';
count++;

constitute the body of the while statement. The curly braces are necessary since more than one statement
makes up the body.
The while statement has the general form:

while ( condition )

statement

• The reserved word while begins the while statement.


• The Boolean expression condition determines whether the body will be (or will continue to be) exe-
cuted. The expression must be enclosed within parentheses as shown.
• The statement is the statement to be executed while the Boolean expression is true. The statement
makes up the body of the while statement. The statement may be a compound statement (multiple
statements enclosed within curly braces, see Section 5.4).
124
Fundamentals of C++ Programming
6.1. THE WHILE STATEMENT 125

Figure 6.1 while flowchart for Listing 6.2 (iterativecounttofive.cpp)

Start

Set count
equal to 1

Is no
count ≤ 5?

yes

Print count’s
value

Increment
count's value

Stop

Except for using the reserved word while instead of if, a while statement looks identical to an if
statement. Sometimes beginning programmers confuse the two or accidentally type if when they mean
while or vice-versa. Usually the very different behavior of the two statements reveals the problem imme-
diately; however, sometimes, especially in nested complex logic, this mistake can be hard to detect.
Figure 6.1 shows how program execution flows through Listing 6.2 (iterativecounttofive.cpp).
The program checks the while’s condition before executing the body, and then re-checks the condition
each time after it executes the body. If the condition is initially false the program’s execution skips the
body completely and continues executing the statements that follow the while’s body. If the condition is
initially true, the program repeatedly executes the body until the condition becomes false, at which point
the loop terminates. Program execution then continues with the statements that follow the loop’s body, if
any. Observe that the body may never be executed if the Boolean expression in the condition is initially
false.
Listing 6.3 (countup.cpp) counts up from zero as long as the user wishes to do so.

Listing 6.3: countup.cpp

125
Fundamentals of C++ Programming
6.1. THE WHILE STATEMENT 126

/*
* Counts up from zero. The user continues the count by entering
* 'Y'. The user discontinues the count by entering 'N'.
*/

#include <iostream>

int main() {
char input; // The users choice
int count = 0; // The current count
bool done = false; // We are not done

while (!done) {

// Print the current value of count


std::cout << count << '\n';
std::cout << "Please enter \"Y\" to continue or \"N\" to quit: ";
std::cin >> input;
// Check for "bad" input
if (input != 'Y' && input != 'y' && input != 'N' && input != 'n')
std::cout << "\"" << input << "\""
<< " is not a valid choice" << '\n';
else if (input == 'Y' || input == 'y')
count++; // Keep counting
else if (input == 'N' || input == 'n')
done = true; // Quit the loop
}
}

A sample run of Listing 6.3 (countup.cpp) produces


0
Please enter "Y" to continue or "N" to quit: y
1
Please enter "Y" to continue or "N" to quit: y
2
Please enter "Y" to continue or "N" to quit: y
3
Please enter "Y" to continue or "N" to quit: q
"q" is not a valid choice
3
Please enter "Y" to continue or "N" to quit: r
"r" is not a valid choice
3
Please enter "Y" to continue or "N" to quit: W
"W" is not a valid choice
3
Please enter "Y" to continue or "N" to quit: Y
4
Please enter "Y" to continue or "N" to quit: y
5
Please enter "Y" to continue or "N" to quit: n

In Listing 6.3 (countup.cpp) the expression


126
Fundamentals of C++ Programming
6.1. THE WHILE STATEMENT 127

input != 'Y' && input != 'y' && input != 'N' && input != 'n'

is true if the character variable input is not equal to one of the listed character literals. The Boolean
variable done controls the loop’s execution. It is important to note that the expression
!done

inside the while’s condition evaluates to the opposite truth value of the variable done; the expression
does not affect the value of done. In other words, the ! operator applied to a variable does not modify the
variable’s value. In order to actually change the variable done, you would need to reassign it, as in
done = !done; // Invert the truth value

For Listing 6.3 (countup.cpp) we have no need to invert its value. We ensure that its value is false initially
and then make it true when the user enters a capital or lower-case N.
Listing 6.4 (addnonnegatives.cpp) is a program that allows a user to enter any number of nonnegative
integers. When the user enters a negative value, the program no longer accepts input, and it displays the
sum of all the nonnegative values. If a negative number is the first entry, the sum is zero.

Listing 6.4: addnonnegatives.cpp


/*
* Allow the user to enter a sequence of nonnegative
* integers. The user ends the list with a negative
* integer. At the end the sum of the nonnegative
* integers entered is displayed. The program prints
* zero if the user enters no nonnegative integers.
*/

#include <iostream>

int main() {
int input = 0, // Ensure the loop is entered
sum = 0; // Initialize sum

// Request input from the user


std::cout << "Enter numbers to sum, negative number ends list:";

while (input >= 0) { // A negative number exits the loop


std::cin >> input; // Get the value
if (input >= 0)
sum += input; // Only add it if it is nonnegative
}
std::cout << "Sum = " << sum << '\n'; // Display the sum
}

The initialization of input to zero coupled with the condition input >= 0 of the while guarantees
that program will execute the body of the while loop at least once. The if statement ensures that a
negative entry will not be added to sum. (Could the condition have used > instead of >= and achieved the
same results?) When the user enters a negative integer the program will not update sum, and the condition
of the while will no longer be true. The program’s execution then leaves the loop and executes the print
statement at the end.
Listing 6.4 (addnonnegatives.cpp) shows that a while loop can be used for more than simple counting.
127
Fundamentals of C++ Programming
6.1. THE WHILE STATEMENT 128

The program does not keep track of the number of values entered. The program simply accumulates the
entered values in the variable named sum.
It is a little awkward in Listing 6.4 (addnonnegatives.cpp) that the same condition appears twice, once
in the while and again in the if. Furthermore, what if the user wishes to enter negative values along with
nonnegative values? We can simplify the code with a common C++ idiom that uses std::cin and the
extraction operator as a condition within a while statement.
If x is an integer, the expression
std::cin >> x

evaluates to false if the user does not enter a valid integer literal. Armed with this knowledge we can
simplify and enhance Listing 6.4 (addnonnegatives.cpp) as shown in Listing 6.5 (addnumbers.cpp).

Listing 6.5: addnumbers.cpp


#include <iostream>

int main() {
int input, sum = 0;
std::cout << "Enter numbers to sum, type 'q' to end the list:";
while (std::cin >> input)
sum += input;
std::cout << "Sum = " << sum << '\n';
}

The condition reads a value from the input stream and, if it is successful, it is interpreted as true.
When the user enters 'q', the loop is terminated. If the user types 'q' at the beginning, the loop is not
entered. The if statement is no longer necessary, since the statement
sum += input;

can be executed only if input has been legitimately assigned. Also, the variable input no longer needs
to initialized with a value simply so the loop is entered the first time; now it is assigned and then checked
within the condition of the while.
In Listing 6.5 (addnumbers.cpp), the program’s execution will terminate with any letter the user types;
an entry of 'x' or Ctrl-Z will terminate the sequence just as well as 'q'.

If you use the


while (std::cin >> x) {
// Do something . . .
}
idiom, be aware that when the loop is exited due to the input stream being unable
to read a value of the type of variable x, the std::cin input stream object is left
in an error state and cannot be used for the rest of the program’s execution. It you
wish to use this technique and reuse std::cin later, you must reset std::cin
and extract and discard keystrokes entered since the last valid use of the extractor
operator. This recovery process is covered in Section 13.2, but, for now, use this
idiom to control a loop only if the program does not require additional user input
later during its execution.

128
Fundamentals of C++ Programming
6.1. THE WHILE STATEMENT 129

Listing 6.6 (powersof10.cpp) prints the powers of 10 from 1 to 1,000,000,000 (the next power of ten,
10,000,000,000, is outside the range of the int type).

Listing 6.6: powersof10.cpp


#include <iostream>

int main() {
int power = 1;
while (power <= 1000000000) {
std::cout << power << '\n';
power *= 10;
}
}

Listing 6.6 (powersof10.cpp) produces


1
10
100
1000
10000
100000
1000000
10000000
100000000
1000000000

It is customary to right justify a column of numbers, but Listing 6.6 (powersof10.cpp) prints the powers
of ten with their most-significant digit left aligned. We can right align the numbers using a stream object
called a steam manipulator. The specific stream manipulator we need is named std::setw. setw means
“set width.” It can be used as
std::cout << std::setw(3) << x << '\n';

This statement prints the value of x right justified within a three character horizontal space on the screen.
Listing 6.7 (powersof10justified.cpp) shows the affects of setw.

Listing 6.7: powersof10justified.cpp


#include <iostream>
#include <iomanip>

// Print the powers of 10 from 1 to 1,000,000,000


int main() {
int power = 1;
while (power <= 1000000000) {
// Right justify each number in a field 10 wide
std::cout << std::setw(10) << power << '\n';
power *= 10;
}
}

Listing 6.7 (powersof10justified.cpp) prints


129
Fundamentals of C++ Programming
6.1. THE WHILE STATEMENT 130

1
10
100
1000
10000
100000
1000000
10000000
100000000
1000000000

Observe that in order to use setw the compiler needs to be made aware of it. The needed information about
std::setw is not found in the iostream header file, so an additional preprocessor include directive is
required:
#include <iomanip>

The std::setw manipulator “conditions” the output stream for the next item to be printed. The val-
ues passed to the “conditioned” stream are all right justified within the number of spaces specified by
std::setw.
As an aside, this is good place to reveal another trick to improve the output of a C++ program. List-
ing 6.8 (powersof10withcommas.cpp) enhances Listing 6.7 (powersof10justified.cpp) by adding commas in
the appropriate places in the displayed numbers.

Listing 6.8: powersof10withcommas.cpp


#include <iostream>
#include <iomanip>
#include <locale>

// Print the powers of 10 from 1 to 1,000,000,000


int main() {
int power = 1;
std::cout.imbue(std::locale("en_US.utf8"));
while (power <= 1000000000) {
// Right justify each number in a field 10 wide
std::cout << std::setw(13) << power << '\n';
power *= 10;
}
}

Listing 6.8 (powersof10withcommas.cpp) prints


1
10
100
1,000
10,000
100,000
1,000,000
10,000,000
100,000,000
1,000,000,000
130
Fundamentals of C++ Programming
6.1. THE WHILE STATEMENT 131

Figure 6.2 Decision tree for troubleshooting a computer system Listing 6.9 (troubleshootloop.cpp)

The statement
std::cout.imbue(std::locale("en_US.utf8"));

adjusts the std::cout object to print number with digit separators appropriate for United States English.
If you replace the statement with
std::cout.imbue(std::locale("german"));

the output becomes

1
10
100
1.000
10.000
100.000
1.000.000
10.000.000
100.000.000
1.000.000.000

because the German language uses periods in place of commas for digit separators. The "german" locale
also would display a comma where the decimal point would appear in an English floating-point number.
We can use a while statement to make Listing 5.12 (troubleshoot.cpp) more convenient for the user.
Recall that the computer troubleshooting program forces the user to rerun the program once a potential
program has been detected (for example, turn on the power switch, then run the program again to see what
else might be wrong). A more desirable decision logic is shown in Figure 6.2.
131
Fundamentals of C++ Programming
6.1. THE WHILE STATEMENT 132

Listing 6.9 (troubleshootloop.cpp) incorporates a while statement so that the program’s execution
continues until the problem is resolved or its resolution is beyond the capabilities of the program.

Listing 6.9: troubleshootloop.cpp


#include <iostream>

int main() {
std::cout << "Help! My computer doesn't work!\n";
char choice;
bool done = false; // Initially, we are not done

while (!done) { // Continue until we are done


std::cout << "Does the computer make any sounds "
<< "(fans, etc.) or show any lights? (y/n):";
std::cin >> choice;
// The troubleshooting control logic
if (choice == 'n') { // The computer does not have power
std::cout << "Is it plugged in? (y/n):";
std::cin >> choice;
if (choice == 'n') { // It is not plugged in, plug it in
std::cout << "Plug it in.\n";
}
else { // It is plugged in
std::cout << "Is the switch in the \"on\" position? (y/n):";
std::cin >> choice;
if (choice == 'n') { // The switch is off, turn it on!
std::cout << "Turn it on.\n";
}
else { // The switch is on
std::cout << "Does the computer have a fuse? (y/n):";
std::cin >> choice;
if (choice == 'n') { // No fuse
std::cout << "Is the outlet OK? (y/n):";
std::cin >> choice;
if (choice == 'n') { // Fix outlet
std::cout << "Check the outlet's circuit "
<< "breaker or fuse. Move to a "
<< "new outlet, if necessary.\n";
}
else { // Beats me!
std::cout << "Please consult a service "
<< "technician.\n";
done = true; // Exhausted simple fixes
}
}
else { // Check fuse
std::cout << "Check the fuse. Replace if "
<< "necessary.\n";
}
}
}
}
else { // The computer has power
std::cout << "Please consult a service technician.\n";
132
Fundamentals of C++ Programming
6.2. NESTED LOOPS 133

done = true; // Only troubleshoots power issues


}
}
}

The bulk of the body of the Listing 6.9 (troubleshootloop.cpp)is wrapped by a while statement. The
Boolean variable done is often called a flag. You can think of the flag being down when the value is
false and raised when it is true. In this case, when the flag is raised, it is a signal that the program should
terminate.
Notice the last 11 lines of Listing 6.9 (troubleshootloop.cpp):
}
}
}
}
else { // The computer has power
std::cout << "Please consult a service technician.\n";
done = true; // Only troubleshoots power issues
}
}
}

In the way this code is organized, the matching opening curly brace of a particular closing curly brace can be
found by scanning upward in the source code until the closest opening curly brace at the same indentation
level is found. Our programming logic is now getting complex enough that the proper placement of curly
braces is crucial for human readers to more quickly decipher how the program should work. See Section 4.5
for guidelines on indentation and curly brace placement to improve code readability.

6.2 Nested Loops

Just like in if statements, while bodies can contain arbitrary C++ statements, including other while
statements. A loop can therefore be nested within another loop. To see how nested loops work, consider a
program that prints out a multiplication table. Elementary school students use multiplication tables, or times
tables, as they learn the products of integers up to 10 or even 12. Figure 6.3 shows a 10 × 10 multiplication
table. We want our multiplication table program to be flexible and allow the user to specify the table’s size.
We will begin our development work with a simple program and add features as we go. First, we will not
worry about printing the table’s row and column titles, nor will we print the lines separating the titles from
the contents of the table. Initially we will print only the contents of the table. We will see we need a nested
loop to print the table’s contents, but that still is too much to manage in our first attempt. In our first attempt
we will print the rows of the table in a very rudimentary manner. Once we are satisfied that our simple
program works we can add more features. Listing 6.10 (timestable-1st-try.cpp) shows our first attempt at a
muliplication table.

Listing 6.10: timestable-1st-try.cpp


#include <iostream>

int main() {
int size; // The number of rows and columns in the table
std::cout << "Please enter the table size: ";
133
Fundamentals of C++ Programming
6.2. NESTED LOOPS 134

Figure 6.3 A 10 × 10 multiplication table

std::cin >> size;


// Print a size x size multiplication table
int row = 1;
while (row <= size) { // Table has 10 rows.
std::cout << "Row #" << row << '\n';
row++; // Next row
}
}

The output of Listing 6.10 (timestable-1st-try.cpp) is somewhat underwhelming:


Please enter the table size: 10
Row #1
Row #2
Row #3
Row #4
Row #5
Row #6
Row #7
Row #8
Row #9
Row #10

Listing 6.10 (timestable-1st-try.cpp) does indeed print each row in its proper place—it just does not
supply the needed detail for each row. Our next step is to refine the way the program prints each row. Each
row should contain size numbers. Each number within each row represents the product of the current row
and current column; for example, the number in row 2, column 5 should be 2 × 5 = 10. In each row,
therefore, we must vary the column number from from 1 to size. Listing 6.11 (timestable-2nd-try.cpp)
contains the needed refinement.
Listing 6.11: timestable-2nd-try.cpp
#include <iostream>

int main() {
134
Fundamentals of C++ Programming
6.2. NESTED LOOPS 135

int size; // The number of rows and columns in the table


std::cout << "Please enter the table size: ";
std::cin >> size;
// Print a size x size multiplication table
int row = 1;
while (row <= size) { // Table has size rows.
int column = 1; // Reset column for each row.
while (column <= size) { // Table has size columns.
int product = row*column; // Compute product
std::cout << product << " "; // Display product
column++; // Next element
}
std::cout << '\n'; // Move cursor to next row
row++; // Next row
}
}

We use a loop to print the contents of each row. The outer loop controls how many total rows the program
prints, and the inner loop, executed in its entirity each time the program prints a row, prints the individual
elements that make up a row.
The result of Listing 6.11 (timestable-2nd-try.cpp) is
Please enter the table size: 10
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100

The numbers within each column are not lined up nicely, but the numbers are in their correct positions rela-
tive to each other. We can use the std::setw stream manipulator introduced in Listing 6.7 (powersof10justified.cpp)
to right justify the numbers within a four-digit area. Listing 6.12 (timestable-3rd-try.cpp) contains this align-
ment adjustment.

Listing 6.12: timestable-3rd-try.cpp


#include <iostream>
#include <iomanip>

int main() {
int size; // The number of rows and columns in the table
std::cout << "Please enter the table size: ";
std::cin >> size;
// Print a size x size multiplication table
int row = 1;
while (row <= size) { // Table has size rows.
int column = 1; // Reset column for each row.
while (column <= size) { // Table has size columns.
int product = row*column; // Compute product
135
Fundamentals of C++ Programming
6.2. NESTED LOOPS 136

std::cout << std::setw(4) << product; // Display product


column++; // Next element
}
std::cout << '\n'; // Move cursor to next row
row++; // Next row
}
}

Listing 6.12 (timestable-3rd-try.cpp) produces the table’s contents in an attractive form:


Please enter the table size: 10
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100

Input values of 5:
Please enter the table size: 5
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25

and 15:
Please enter the table size: 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
3 6 9 12 15 18 21 24 27 30 33 36 39 42 45
4 8 12 16 20 24 28 32 36 40 44 48 52 56 60
5 10 15 20 25 30 35 40 45 50 55 60 65 70 75
6 12 18 24 30 36 42 48 54 60 66 72 78 84 90
7 14 21 28 35 42 49 56 63 70 77 84 91 98 105
8 16 24 32 40 48 56 64 72 80 88 96 104 112 120
9 18 27 36 45 54 63 72 81 90 99 108 117 126 135
10 20 30 40 50 60 70 80 90 100 110 120 130 140 150
11 22 33 44 55 66 77 88 99 110 121 132 143 154 165
12 24 36 48 60 72 84 96 108 120 132 144 156 168 180
13 26 39 52 65 78 91 104 117 130 143 156 169 182 195
14 28 42 56 70 84 98 112 126 140 154 168 182 196 210
15 30 45 60 75 90 105 120 135 150 165 180 195 210 225

also give good results.


All that is left is to add the row and column titles and the lines that bound the edges of the table.
Listing 6.13 (timestable.cpp) adds the necessary code.
136
Fundamentals of C++ Programming
6.2. NESTED LOOPS 137

Listing 6.13: timestable.cpp


#include <iostream>
#include <iomanip>

int main() {
int size; // The number of rows and columns in the table
std::cout << "Please enter the table size: ";
std::cin >> size;
// Print a size x size multiplication table

// First, print heading: 1 2 3 4 5 etc.


std::cout << " ";
// Print column heading
int column = 1;
while (column <= size) {
std::cout << std::setw(4) << column; // Print heading for this column.
column++;
}
std::cout << '\n';

// Print line separator: +------------------


std::cout << " +";
column = 1;
while (column <= size) {
std::cout << "----"; // Print line for this column.
column++;
}
std::cout << '\n';

// Print table contents


int row = 1;
while (row <= size) { // Table has size rows.
std::cout << std::setw(2) << row << " |"; // Print heading for row.
int column = 1; // Reset column for each row.
while (column <= size) { // Table has size columns.
int product = row*column; // Compute product
std::cout << std::setw(4) << product; // Display product
column++; // Next element
}
row++; // Next row
std::cout << '\n'; // Move cursor to next row
}
}

When the user supplies the value 10, Listing 6.13 (timestable.cpp) produces

Please enter the table size: 10


1 2 3 4 5 6 7 8 9 10
+----------------------------------------
1 | 1 2 3 4 5 6 7 8 9 10
2 | 2 4 6 8 10 12 14 16 18 20
3 | 3 6 9 12 15 18 21 24 27 30
4 | 4 8 12 16 20 24 28 32 36 40
5 | 5 10 15 20 25 30 35 40 45 50
137
Fundamentals of C++ Programming
6.2. NESTED LOOPS 138

6 | 6 12 18 24 30 36 42 48 54 60
7 | 7 14 21 28 35 42 49 56 63 70
8 | 8 16 24 32 40 48 56 64 72 80
9 | 9 18 27 36 45 54 63 72 81 90
10 | 10 20 30 40 50 60 70 80 90 100

An input of 15 yields
Please enter the table size: 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+------------------------------------------------------------
1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
2 | 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
3 | 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45
4 | 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60
5 | 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75
6 | 6 12 18 24 30 36 42 48 54 60 66 72 78 84 90
7 | 7 14 21 28 35 42 49 56 63 70 77 84 91 98 105
8 | 8 16 24 32 40 48 56 64 72 80 88 96 104 112 120
9 | 9 18 27 36 45 54 63 72 81 90 99 108 117 126 135
10 | 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150
11 | 11 22 33 44 55 66 77 88 99 110 121 132 143 154 165
12 | 12 24 36 48 60 72 84 96 108 120 132 144 156 168 180
13 | 13 26 39 52 65 78 91 104 117 130 143 156 169 182 195
14 | 14 28 42 56 70 84 98 112 126 140 154 168 182 196 210
15 | 15 30 45 60 75 90 105 120 135 150 165 180 195 210 225

If the user enters 7, the program prints


Please enter the table size: 7
1 2 3 4 5 6 7
+----------------------------
1 | 1 2 3 4 5 6 7
2 | 2 4 6 8 10 12 14
3 | 3 6 9 12 15 18 21
4 | 4 8 12 16 20 24 28
5 | 5 10 15 20 25 30 35
6 | 6 12 18 24 30 36 42
7 | 7 14 21 28 35 42 49

The user even can enter a 1:


Please enter the table size: 1
1
+----
1 | 1

As we can see, the table automatically adjusts to the size and spacing required by the user’s input.
This is how Listing 6.13 (timestable.cpp) works:

• It is important to distinguish what is done only once (outside all loops) from that which is done
repeatedly. The column heading across the top of the table is outside of all the loops; therefore, it is
printed all at once.
138
Fundamentals of C++ Programming
6.2. NESTED LOOPS 139

• The work to print the heading for the rows is distributed throughout the execution of the outer loop.
This is because the heading for a given row cannot be printed until all the results for the previous row
have been printed.
• In the nested loop, row is the control variable for the outer loop; column controls the inner loop.
• The inner loop executes size times on every single iteration of the outer loop. How many times is
the statement
std::cout << product << " "; // Display product

executed? size × size times, one time for every product in the table.
• A newline is printed after the contents of each row is displayed; thus, all the values printed in the
inner (column) loop appear on the same line.

Nested loops are used when an iterative process itself must be repeated. In our times table example, a
while loop is used to print the contents of each row, but multiple rows must be printed. The inner loop
prints the contents of each row, while the outer is responsible for printing all the rows.
Listing 6.14 (permuteabc.cpp) uses a triply-nested loop to print all the different arrangements of the
letters A, B, and C. Each string printed is a permutation of ABC.

Listing 6.14: permuteabc.cpp


// File permuteabc.cpp

#include <iostream>

int main() {
char first = 'A'; // The first letter varies from A to C
while (first <= 'C') {
char second = 'A';
while (second <= 'C') { // The second varies from A to C
if (second != first) { // No duplicate letters
char third = 'A';
while (third <= 'C') { // The third varies from A to C
// Don't duplicate first or second letter
if (third != first && third != second)
std::cout << first << second << third << '\n';
third++;
}
}
second++;
}
first++;
}
}

Notice how the if statements are used to prevent duplicate letters within a given string. The output of
Listing 6.14 (permuteabc.cpp) is all six permutations of ABC:
ABC
ACB
BAC
BCA
139
Fundamentals of C++ Programming
6.3. ABNORMAL LOOP TERMINATION 140

CAB
CBA

6.3 Abnormal Loop Termination

By default, a while statement executes until its condition becomes false. The executing program checks
this condition only at the “top” of the loop. This means that even if the Boolean expression that makes up
the condition becomes false before the program completes executing all the statements within the body of
the loop, all the remaining statements in the loop’s body must complete before the loop can once again
check its condition. In other words, the while statement in and of itself cannot exit its loop somewhere in
the middle of its body.
Ordinarily this behavior is not a problem. Usually the intention is to execute all the statements within
the body as an indivisible unit. Sometimes, however, it is desirable to immediately exit the body or recheck
the condition from the middle of the loop instead. C++ provides the break and continue statements to
to give programmers more flexibility designing the control logic of loops.

6.3.1 The break statement

C++ provides the break statement to implement middle-exiting control logic. The break statement
causes the immediate exit from the body of the loop. Listing 6.15 (addmiddleexit.cpp) is a variation of
Listing 6.4 (addnonnegatives.cpp) that illustrates the use of break.

Listing 6.15: addmiddleexit.cpp


#include <iostream>

int main() {
int input, sum = 0;
std::cout << "Enter numbers to sum, negative number ends list:";
while (true) {
std::cin >> input;
if (input < 0)
break; // Exit loop immediately
sum += input;
}
std::cout << "Sum = " << sum << '\n';
}

The condition of the while in Listing 6.15 (addmiddleexit.cpp) is a tautology. This means the condition
is true and can never be false. When the program’s execution reaches the while statement it is guaranteed
to enter the loop’s body and the while loop itself does not provide a way of escape. The if statement in
the loop’s body:
if (input < 0) // Is input negative
break; // If so, exit the loop immediately

provides the necessary exit. In this case the break statement, executed conditionally based on the value of
the variable input, exits the loop. In Listing 6.15 (addmiddleexit.cpp) the break statement executes only
when the user enters a negative number. When the program’s execution encounters the break statement,
140
Fundamentals of C++ Programming
6.3. ABNORMAL LOOP TERMINATION 141

Figure 6.4 The code on the left generically represents any loop that uses a break statement. It is possible
to transform the code on the left to eliminate the break statement, as the code on the right shows.

bool looping = true;


while ( Condition 1 ) { while ( looping && Condition 1 ) {

Part A Part A

if ( Condition 2 ) { if ( Condition 2 ) {

Part B Eliminate Part B


the
break; looping = false;
break
} }
statement
else {
Part C
Part C
}
}
}

it immediately jumps out of the loop. It skips any statements following the break within the loop’s body.
Since the statement
sum += input; // Accumulate user input

appears after the break, it is not possible for the program to add a negative number to the sum variable.
Some software designers believe that programmers should use the break statement sparingly because
it deviates from the normal loop control logic. Ideally, every loop should have a single entry point and
single exit point. While Listing 6.15 (addmiddleexit.cpp) has a single exit point (the break statement),
some programmers commonly use break statements within while statements in the which the condition
for the while is not a tautology. Adding a break statement to such a loop adds an extra exit point (the
top of the loop where the condition is checked is one point, and the break statement is another). Using
multiple break statements within a single loop is particularly dubious and you should avoid that practice.
Why have the break statement at all if its use is questionable and it is dispensable? The logic in
Listing 6.4 (addnonnegatives.cpp) is fairly simple, so the restructuring of Listing 6.15 (addmiddleexit.cpp)
is straightforward; in general, the effort to restructure code to avoid a break statement may complicate
the logic a bit and require the introduction of an additional Boolean variable. As shown in Figure 6.4, any
program that uses a break statement can be rewritten so that the break statement is not used.
The no-break version introduces a Boolean variable, and the loop control logic is a little more com-
plicated. The no-break version uses more memory (an extra variable) and more time to execute (requires
an extra check in the loop condition during every iteration of the loop). This extra memory is insignificant,
and except for rare, specialized applications, the extra execution time is imperceptible. In most cases, the
more important issue is that the more complicated the control logic for a given section of code, the more
difficult the code is to write correctly. In some situations, even though it violates the “single entry point,
single exit point” principle, a simple break statement is an acceptable loop control option.
141
Fundamentals of C++ Programming
6.3. ABNORMAL LOOP TERMINATION 142

6.3.2 The goto Statement

The break statement exits the single loop in which it is located. A break statement is insufficient to jump
completely out of the middle of a nested loop. The goto statement allows the program’s execution flow
to jump to a specified location within the function. Listing 6.16 (exitnested.cpp) uses a goto statement to
jump out from the middle of a nested loop.

Listing 6.16: exitnested.cpp


#include <iostream>

int main() {
// Compute some products
int op1 = 2;
while (op1 < 100) {
int op2 = 2;
while (op2 < 100) {
if (op1 * op2 == 3731)
goto end;
std::cout << "Product is " << (op1 * op2) << '\n';
op2++;
}
op1++;
}
end:
std::cout << "The end" << '\n';
}

When op1 * op2 is 3731, program flow will jump to the specified label within the program. In this
example, the label is named end, but this name is arbitrary. Like variable names, label names should be
chosen to indicate their intended purpose. The label here named end comes after and outside the nested
while loops.
A label’s name is an identifier (see Section 3.3), and a label is distinguished by the colon that immedi-
ately follows its name. A label represents a target to which a goto can jump. A goto label must appear
before a statement within a function.
With the goto statement, the while is superfluous; for example, Listing 6.2 (iterativecounttofive.cpp)
could be rewritten without the while statement as shown in Listing 6.17 (gotoloop.cpp).

Listing 6.17: gotoloop.cpp


#include <iostream>

int main() {
int count = 1; // Initialize counter
top:
if (count > 5)
goto end;
std::cout << count << '\n'; // Display counter, then
count++; // Increment counter
goto top;
end:
; // Target is an empty statement
}
142
Fundamentals of C++ Programming
6.3. ABNORMAL LOOP TERMINATION 143

Early programming languages like FORTRAN and early versions of BASIC did not have structured
statements like while, so programmers were forced to use goto statements to write loops. The problem
with using goto statements is that it is easy to develop program logic that is very difficult to understand,
even for the original author of the code. See the Wikipedia article about spaghetti code (http://en.
wikipedia.org/wiki/Spaghetti_code). The structured programming revolution of the 1960s
introduced constructs such as the while statement and resulted in the disappearance of the use of goto
in most situations. All modern programming languages have a form of the while statement, so the goto
statement in C++ is largely ignored except for the case of breaking out of a nested loop. You similarly
should restrict your use of the goto statement to the abnormal exit of nested loops.

6.3.3 The continue Statement

When a program’s execution encounters a break statement inside a loop, it skips the rest of the body
of the loop and exits the loop. The continue statement is similar to the break statement, except the
continue statement does not necessarily exit the loop. The continue statement skips the rest of the
body of the loop and immediately checks the loop’s condition. If the loop’s condition remains true, the
loop’s execution resumes at the top of the loop. Listing 6.18 (continueexample.cpp) shows the continue
statement in action.

Listing 6.18: continueexample.cpp


#include <iostream>

int main() {
int input, sum = 0;
bool done = false;
while (!done) {
std::cout << "Enter positive integer (999 quits): ";
std::cin >> input;
if (input < 0) {
std::cout << "Negative value " << input << " ignored\n";
continue; // Skip rest of body for this iteration
}
if (input != 999) {
std::cout << "Tallying " << input << '\n';
sum += input;
}
else
done = (input == 999); // 999 entry exits loop
}
std::cout << "sum = " << sum << '\n';
}

Programmers do not use the continue statement as frequently as the break statement since it is easy
to transform code using continue into an equivalent form that does not. Listing 6.19 (nocontinueexample.cpp)
works exactly like Listing 6.18 (continueexample.cpp), but it avoids the continue statement.

Listing 6.19: nocontinueexample.cpp


#include <iostream>

int main() {
int input, sum = 0;
143
Fundamentals of C++ Programming
6.3. ABNORMAL LOOP TERMINATION 144

Figure 6.5 The code on the left generically represents any loop that uses a continue statement. It is
possible to transform the code on the left to eliminate the continue statement, as the code on the right
shows.

while ( Condition 1 ) { while ( Condition 1 ) {

Part A Part A

if ( Condition 2 ) { if ( Condition 2 ) {

Part B Eliminate Part B


the
continue; }
continue
} else {
statement
Part C
Part C
}
}
}

bool done = false;


while (!done) {
std::cout << "Enter positive integer (999 quits): ";
std::cin >> input;
if (input < 0)
std::cout << "Negative value " << input << " ignored\n";
else
if (input != 999) {
std::cout << "Tallying " << input << '\n';
sum += input;
}
else
done = (input == 999); // 999 entry exits loop
}
std::cout << "sum = " << sum << '\n';
}

Figure 6.5 shows how we can rewrite any program that uses a continue statement into an equivalent
form that does not use continue. The transformation is simpler than for break elimination (see Fig-
ure 6.4) since the loop’s condition remains the same, and no additional variable is needed.
The version that uses continue is no more efficient than the version that uses else; in fact, the Vi-
sual C++ and GNU C++ compilers generate the same machine language code for Listing 6.18 (continueexample.cpp)
and Listing 6.19 (nocontinueexample.cpp). Also, the logic of the else version is no more complex than
the continue version. Therefore, unlike the break statement above, there is no compelling reason to use
the continue statement. Sometimes a programmer may add a continue statement at the last minute
144
Fundamentals of C++ Programming
6.4. INFINITE LOOPS 145

to an existing loop body to handle an exceptional condition (like ignoring negative numbers in the example
above) that initially went unnoticed. If the body of the loop is lengthy, the programmer can add a condi-
tional statement with a continue near the top of the loop body without touching the logic of the rest of
the loop. The continue statement thus merely provides a convenient alternative for the programmer. The
else version is preferred.

6.4 Infinite Loops

An infinite loop is a loop without an exit. Once the program flow enters an infinite loop’s body it cannot
escape. Some infinite loops are by design; for example, a long-running server application, like a Web server,
may need to continuously check for incoming connections. This server application can perform this check-
ing within a loop that runs indefinitely. All too often, however, beginning programmers create infinite loops
by accident, and these infinite loops represent logic errors in their programs.
Intentional infinite loops should be made obvious. For example,
while (true) {
/* Do something forever . . . */
}

The Boolean literal true is always true, so it is impossible for the loop’s condition to be false. The only
ways to exit the loop is via a break statement, return statement (see Chapter 9), or an exit call (see
Section 8.1) embedded somewhere within its body.
It is easy to write an intentional infinite loop. Accidental infinite loops are quite common, but can be
puzzling for beginning programmers to diagnose and repair. Consider Listing 6.20 (findfactors.cpp) that
attempts to print the integers from 1 to 20 along with their associated factors.

Listing 6.20: findfactors.cpp


#include <iostream>

int main() {
// List of the factors of the numbers up to 20
int n = 1;
const int MAX = 20;
while (n <= MAX) {
int factor = 1;
std::cout << n << ": ";
while (factor <= n)
if (n % factor == 0) {
std::cout << factor << " ";
factor++;
}
std::cout << '\n'; // Go to next line for next n
n++;
}
}

It displays
1: 1
2: 1 2
145
Fundamentals of C++ Programming
6.4. INFINITE LOOPS 146

3: 1

and then “freezes up” or “hangs,” ignoring any user input (except the key sequence Ctrl C on most sys-
tems which interrupts and terminates the running program). This type of behavior is a frequent symptom
of an unintentional infinite loop. The factors of 1 display properly, as do the factors of 2. The first fac-
tor of 3 is properly displayed and then the program hangs. Since the program is short, the problem may
be easy to locate. In some programs, though, the error may be challenging to find. Even in Listing 6.20
(findfactors.cpp) the debugging task is nontrivial since it involves nested loops. (Can you find and fix the
problem in Listing 6.20 (findfactors.cpp) before reading further?)
In order to avoid infinite loops, we must ensure that the loop exhibits certain properties:

• The loop’s condition must not be a tautology (a Boolean expression that can never be false). For
example,
while (i >= 1 || i <= 10) {
/* Body omitted */
}

is an infinite loop since any value chosen for i will satisfy one or both of the two subconditions.
Perhaps the programmer intended to use a && instead of || to stay in the loop as long as i remains
in the range 1...10.
In Listing 6.20 (findfactors.cpp) the outer loop condition is
n <= MAX

If n is 21 and MAX is 20, then the condition is false, so this is not a tautology. Checking the inner loop
condition:
factor <= n

we see that if factor is 3 and n is 2, then the expression is false; therefore, it also is not a tautology.
• The condition of a while must be true initially to gain access to its body. The code within the body
must modify the state of the program in some way so as to influence the outcome of the condition
that is checked at each iteration. This usually means code within the body of the loop modifies one of
the variables used in the condition. Eventually the variable assumes a value that makes the condition
false, and the loop terminates.
In Listing 6.20 (findfactors.cpp) the outer loop’s condition involves the variable n and constant MAX.
MAX cannot change, so to avoid an infinite loop it is essential that n be modified within the loop.
Fortunately, the last statement in the body of the outer loop increments n. n is initially 1 and MAX is
20, so unless the circumstances arise to make the inner loop infinite, the outer loop should eventually
terminate.
The inner loop’s condition involves the variables n and factor. No statement in the inner loop
modifies n, so it is imperative that factor be modified in the loop. The good news is factor is
incremented in the body of the inner loop, but the bad news is the increment operation is protected
within the body of the if statement. The inner loop contains one statement, the if statement. That
if statement in turn has two statements in its body:
while (factor <= n)
if (n % factor == 0) {
std::cout << factor << " ";
factor++;
}
146
Fundamentals of C++ Programming
6.4. INFINITE LOOPS 147

If during the program’s execution the condition of the if is ever false (for example, when n is 3 and
factor is 2), the inner loop cannot change the state of the program. This means factor does not
change, and factor controls the progression of the inner loop. This effectively creates an infinite
loop for the inner while loop. We must move the statement that modifies the factor variable
outside of the if statement’s body:
while (factor <= n) {
if (n % factor == 0)
std::cout << factor << " ";
factor++;
}

Note that the curly braces are necessary for the statement incrementing factor to be part of the body
of the while. Now the inner while statement body contains two statements: the if statement
and the statement incrementing factor. This new version runs correctly because factor changes
during each iteration of the inner while loop regardless of the truth value of the condition in the if
statement.

Programmers can use a debugger to step through a program to see where and why an infinite loop
arises. Another common technique is to put print statements in strategic places to examine the values of the
variables involved in the loop’s control. The original inner loop can be so augmented:
while (factor <= n) {
std::cout << "factor = " << factor
<< " n = " << n << '\n';
if (n % factor == 0) {
std::cout << factor << " ";
factor++;
}
}

It produces the following output:


1: factor = 1 n = 1
1
2: factor = 1 n = 2
1 factor = 2 n = 2
2
3: factor = 1 n = 3
1 factor = 2 n = 3
factor = 2 n = 3
factor = 2 n = 3
factor = 2 n = 3
factor = 2 n = 3
factor = 2 n = 3
.
.
.

The program continues to print the same line until the user interrupts its execution. The output demonstrates
that once factor becomes equal to 2 and n becomes equal to 3 the program’s execution becomes trapped
in the inner loop. Under these conditions:

1. 2 < 3 is true, so the loop continues and


147
Fundamentals of C++ Programming
6.5. ITERATION EXAMPLES 148

2. 3 % 2 is equal to 1, so the if statement will not increment factor.

It is imperative that factor be incremented each time through the inner loop; therefore, the statement
incrementing factor must be moved outside of the if’s guarded body.

6.5 Iteration Examples

We can implement some sophisticated algorithms in C++ now that we are armed with if and while
statements. This section provides several examples that show off the power of conditional execution and
iteration.

6.5.1 Drawing a Tree

Suppose we must write a program that draws a triangular tree, and the user provides the tree’s height. A
tree that is five levels tall would look like
*
***
*****
*******
*********

whereas a three-level tree would look like


*
***
*****

If the height of the tree is fixed, we can write the program as a simple variation of Listing 2.4 (arrow.cpp)
which uses only printing statements and no loops. Our program, however, must vary its height and width
based on input from the user.
Listing 6.21 (startree.cpp) provides the necessary functionality.

Listing 6.21: startree.cpp


#include <iostream>

int main() {
int height; // Height of tree
std::cout << "Enter height of tree: ";
std::cin >> height; // Get height from user
int row = 0; // First row, from the top, to draw
while (row < height) { // Draw one row for every unit of height
// Print leading spaces
int count = 0;
while (count < height - row) {
std::cout << " ";
count++;
}
// Print out stars, twice the current row plus one:
148
Fundamentals of C++ Programming
6.5. ITERATION EXAMPLES 149

// 1. number of stars on left side of tree


// = current row value
// 2. exactly one star in the center of tree
// 3. number of stars on right side of tree
// = current row value
count = 0;
while (count < 2*row + 1) {
std::cout << "*";
count++;
}
// Move cursor down to next line
std::cout << '\n';
// Change to the next row
row++;
}
}

When a user runs Listing 6.21 (startree.cpp) and enters 7, the program displays

Enter height of tree: 7


*
***
*****
*******
*********
***********
*************

Listing 6.21 (startree.cpp) uses two sequential while loops both nested within a while loop. The
outer while loop is responsible for drawing one row of the tree each time its body is executed:

• The program will execute the outer while loop’s body as long as the user enters a value greater than
zero; if the user enters zero or less, the program terminates and does nothing. This is the expected
behavior.

• The last statement in the body of the outer while:


row++;

ensures that the variable row increases by one each time through the loop; therefore, it eventually
will equal height (since it initially had to be less than height to enter the loop), and the loop will
terminate. There is no possibility of an infinite loop here.

• The body of the outer loop consists of more than one statement; therefore, the body must be enclosed
within curly braces. Whenever a group of statements is enclosed within curly braces a block is formed.
Any variable declared within a block is local to that block. A variable’s scope (the section of the
source code in which the variable exists and can be used) is from its point of declaration to the end
of the block in which it is declared. For example, the variables height and row are declared in the
block that is main’s body; thus, they are local to main. The variable count is declared within the
block that is the body of the outer while statement; therefore, count is local to the outer while
statement. An attempt to use count outside the body of the outer while statement would be an
error.
149
Fundamentals of C++ Programming
6.5. ITERATION EXAMPLES 150

What does it mean for a variable x to be local to a particular section of code? It means x does not exist
outside its scope. There may be other variables in the program named x, but they are different variables. If
it seems odd that you can have two different variables in the same program with the same name, consider
the fact that there can be two people in the same room with the same name. They are different people, but
they have the same name. Similarly, the meaning of a variable depends on its context, and its name is not
necessarily unique.
The two inner loops play distinct roles:

• The first inner loop prints spaces. The number of spaces printed is equal to the height of the tree the
first time through the outer loop and decreases each iteration. This is the correct behavior since each
succeeding row moving down contains fewer leading spaces but more asterisks.
• The second inner loop prints the row of asterisks that make up the tree. The first time through the
outer loop, row is zero, so no left side asterisks are printed, one central asterisk is printed (the top of
the tree), and no right side asterisks are printed. Each time through the loop the number of left-hand
and right-hand stars to print both increase by one and the same central asterisk is printed; therefore,
the tree grows one wider on each side each line moving down. Observe how the 2*row + 1 value
expresses the needed number of asterisks perfectly.
• While it seems asymmetrical, note that no third inner loop is required to print trailing spaces on the
line after the asterisks are printed. The spaces would be invisible, so there is no reason to print them!

6.5.2 Printing Prime Numbers

A prime number is an integer greater than one whose only factors (also called divisors) are one and itself.
For example, 29 is a prime number (only 1 and 29 divide into it with no remainder), but 28 is not (2, 4, 7,
and 14 are factors of 28). Prime numbers were once merely an intellectual curiosity of mathematicians, but
now they play an important role in cryptography and computer security.
The task is to write a program that displays all the prime numbers up to a value entered by the user.
Listing 6.22 (printprimes.cpp) provides one solution.

Listing 6.22: printprimes.cpp


#include <iostream>

int main() {
int max_value;
std::cout << "Display primes up to what value? ";
std::cin >> max_value;
int value = 2; // Smallest prime number
while (value <= max_value) {
// See if value is prime
bool is_prime = true; // Provisionally, value is prime
// Try all possible factors from 2 to value - 1
int trial_factor = 2;
while (trial_factor < value) {
if (value % trial_factor == 0) {
is_prime = false; // Found a factor
break; // No need to continue; it is NOT prime
}
trial_factor++;
}
150
Fundamentals of C++ Programming
6.5. ITERATION EXAMPLES 151

if (is_prime)
std::cout << value << " "; // Display the prime number
value++; // Try the next potential prime number
}
std::cout << '\n'; // Move cursor down to next line
}

Listing 6.22 (printprimes.cpp), with an input of 90, produces:


Display primes up to what value? 90
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89

The logic of Listing 6.22 (printprimes.cpp) is a little more complex than that of Listing 6.21 (startree.cpp).
The user provides a value for max_value. The main loop (outer while iterates over all the values from
two to max_value:

• Two new variables, local to the body of the outer loop, are introduced: trial_factor and is_prime.
is_prime is initialized to true, meaning value is assumed to be prime unless our tests prove oth-
erwise. trial_factor takes on all the values from two to value - 1 in the inner loop:
int trial_factor = 2;
while (trial_factor < value) {
if (value % trial_factor == 0) {
is_prime = false; // Found a factor
break; // No need to continue; it is NOT prime
}
trial_factor++;
}

The expression value % trial_factor is zero when trial_factor divides into value
with no remainder—exactly when trial_factor is a factor of value. If the executing program
determines that any of the values of trial_factor is a factor of value, then it sets is_prime
to false and exits the loop via the break. If the loop continues to completion, the program never sets
is_prime to false, which means it found no factors and value is indeed prime.
• The if statement after the inner loop:
if (is_prime)
std::cout << value << " "; // Display the prime number

simply checks the status of is_prime. If is_prime is true, then value must be prime, so it
prints value along with an extra space for separation from output it may produce during subsequent
iterations.

Some important questions we can ask include:

1. If the user enters a 2, will it be printed?


In this case max_value = value = 2, so the condition of the outer loop
value <= max_value

is true, since 2 <= 2. is_prime is set to true, but the condition of the inner loop
trial_factor < value
151
Fundamentals of C++ Programming
6.5. ITERATION EXAMPLES 152

is not true (2 is not less than 2). Thus, the inner loop is skipped, is_prime is not changed from true,
and 2 is printed. This behavior is correct because 2 is the smallest prime number (and the only even
prime).
2. if the user enters a number less than 2, is anything printed?
The while condition ensures that values less than two are not considered. The body of the while
will never be entered. Only the newline is printed, and no numbers are displayed. This behavior is
correct.
3. Is the inner loop guaranteed to always terminate?
In order to enter the body of the inner loop, trial_factor must be less than value. value
does not change anywhere in the loop. trial_factor is not modified anywhere in the if state-
ment within the loop, and it is incremented within the loop immediately after the if statement.
trial_factor is, therefore, incremented during each iteration of the loop. Eventually, trial_factor
will equal value, and the loop will terminate.
4. Is the outer loop guaranteed to always terminate?
In order to enter the body of the outer loop, value must be less than or equal to max_value.
max_value does not change anywhere in the loop. value is increased in the last statement within
the body of the outer loop, and value is not modified anywhere else. Since the inner loop is guaran-
teed to terminate as shown in the previous answer, eventually value will exceed max_value and
the loop will end.

We can rearrange the logic of the inner while to avoid the break statement. The current version is:
while (trial_factor < value) {
if (value % trial_factor == 0) {
is_prime = false; // Found a factor
break; // No need to continue; it is NOT prime
}
trial_factor++;
}

We can rewrite it as:


while (is_prime && trial_factor < value) {
is_prime = (value % trial_factor != 0);
trial_factor++; // Try next factor
}

This version without the break introduces a slightly more complicated condition for the while but re-
moves the if statement within its body. is_prime is initialized to true before the loop. Each time through
the loop it is reassigned. trial_factor will become false if at any time value % trial_factor
is zero. This is exactly when trial_factor is a factor of value. If is_prime becomes false, the
loop cannot continue, and if is_prime never becomes false, the loop ends when trial_factor be-
comes equal to value. Due to operator precedence, the parentheses are not necessary. The parentheses do
improve readability, since an expression including both == and != is awkward for humans to parse. When
parentheses are placed where they are not needed, as in
x = (y + 2);

the compiler simply ignores them, so there is no efficiency penalty in the compiled code.
We can shorten the loop even further:
152
Fundamentals of C++ Programming
6.6. EXERCISES 153

while (is_prime && trial_factor < value)


is_prime = (value % trial_factor++ != 0);

This version uses the post-increment operator within the test expression (see Section 4.9). Recall that with
the post-increment operator the value of the variable is used in the surrounding expression (if any), and then
the variable is incremented. Since the while’s body now contains only one statement, the curly braces are
not needed.

6.6 Exercises

1. In Listing 6.4 (addnonnegatives.cpp) could the condition of the if statement have used > instead of
>= and achieved the same results? Why?
2. In Listing 6.4 (addnonnegatives.cpp) could the condition of the while statement have used > instead
of >= and achieved the same results? Why?
3. Use a loop to rewrite the following code fragment so that it uses just one std::cout and one '\n'.
std::cout << 2 << '\n';
std::cout << 4 << '\n';
std::cout << 6 << '\n';
std::cout << 8 << '\n';
std::cout << 10 << '\n';
std::cout << 12 << '\n';
std::cout << 14 << '\n';
std::cout << 16 << '\n';

4. In Listing 6.4 (addnonnegatives.cpp) what would happen if the statement containing std::cin is
moved out of the loop? Is moving the assignment out of the loop a good or bad thing to do? Why?
5. How many asterisks does the following code fragment print?
int a = 0;
while (a < 100) {
std::cout << "*";
a++;
}
std::cout << '\n';

6. How many asterisks does the following code fragment print?


int a = 0;
while (a < 100)
std::cout << "*";
std::cout << '\n';

7. How many asterisks does the following code fragment print?


int a = 0;
while (a > 100) {
std::cout << "*";
a++;
153
Fundamentals of C++ Programming
6.6. EXERCISES 154

}
std::cout << '\n';

8. How many asterisks does the following code fragment print?


int a = 0;
while (a < 100) {
int b = 0;
while (b < 55) {
std::cout << "*";
b++;
}
std::cout << '\n';
}

9. How many asterisks does the following code fragment print?


int a = 0;
while (a < 100) {
if (a % 5 == 0)
std::cout << "*";
a++;
}
std::cout << '\n';

10. How many asterisks does the following code fragment print?
int a = 0;
while (a < 100) {
int b = 0;
while (b < 40) {
if ((a + b) % 2 == 0)
std::cout << "*";
b++;
}
std::cout << '\n';
a++;
}

11. How many asterisks does the following code fragment print?
int a = 0;
while (a < 100) {
int b = 0;
while (b < 100) {
int c = 0;
while (c < 100) {
std::cout << "*";
c++;
}
b++;
}
154
Fundamentals of C++ Programming
6.6. EXERCISES 155

a++;
}
std::cout << '\n';

12. What is printed by the following code fragment?


int a = 0;
while (a < 100)
std::cout << a++;
std::cout << '\n';

13. What is printed by the following code fragment?


int a = 0;
while (a > 100)
std::cout << a++;
std::cout << '\n';

14. Rewrite the following code fragment using a break statement and eliminating the done variable.
Your code should behave identically to this code fragment.
bool done = false;
int n = 0, m = 100;
while (!done && n != m) {
std::cin >> n;
if (n < 0)
done = true;
std::cout << "n = " << n << '\n';
}

15. Rewrite the following code fragment so it eliminates the continue statement. Your new code’s
logic should be simpler than the logic of this fragment.
int x = 100, y;
while (x > 0) {
std::cin >> y;
if (y == 25) {
x--;
continue;
}
std::cin >> x;
std::cout << "x = " << x << '\n';
}

16. Suppose you were given some code from the 1960s in a language that did not support structured
statements like while. Your task is to modernize it and adapt it to C++. The following code fragment
has been adapted to C++ already, but you must now structure it with a while statement to replace
the gotos. Your code should be goto free and still behave identically to this code fragment.
int i = 0;
top: if (i >= 10)
goto end;
155
Fundamentals of C++ Programming
6.6. EXERCISES 156

std::cout << i << '\n';


i++;
goto top;
end:

17. What is printed by the following code fragment?


int a = 0;
while (a < 100);
std::cout << a++;
std::cout << '\n';

18. Write a C++ program that accepts a single integer value entered by the user. If the value entered is
less than one, the program prints nothing. If the user enters a positive integer, n, the program prints
an n × n box drawn with * characters. If the users enters 1, for example, the program prints
*

If the user enters a 2, it prints


**
**

An entry of three yields


***
***
***

and so forth. If the user enters 7, it prints


*******
*******
*******
*******
*******
*******
*******

that is, a 7 × 7 box of * symbols.

19. Write a C++ program that allows the user to enter exactly twenty double-precision floating-point
values. The program then prints the sum, average (arithmetic mean), maximum, and minimum of the
values entered.

20. Write a C++ program that allows the user to enter any number of nonnegative double-precision
floating-point values. The user terminates the input list with any negative value. The program then
prints the sum, average (arithmetic mean), maximum, and minimum of the values entered. The termi-
nating negative value is not used in the computations. If the first number the user supplies is negative,
the program simply prints the text NO NUMBERS PROVIDED.

21. Redesign Listing 6.21 (startree.cpp) so that it draws a sideways tree pointing right; for example, if
the user enters 7, the program would print
156
Fundamentals of C++ Programming
6.6. EXERCISES 157

*
**
***
****
*****
******
*******
******
*****
****
***
**
*

22. Redesign Listing 6.21 (startree.cpp) so that it draws a sideways tree pointing left; for example, if the
user enters 7, the program would print
*
**
***
****
*****
******
*******
******
*****
****
***
**
*

157
Fundamentals of C++ Programming
6.6. EXERCISES 158

158
Fundamentals of C++ Programming
159

Chapter 7

Other Conditional and Iterative


Statements

The if/else and while statements are sufficient to implement any algorithms that involve conditional
execution and looping. The break and continue statements are convenient but are not necessary. C++
provides some additional conditional and iterative statements that are more convenient to use in some
circumstances. These additional statements include

• switch: an alternative to some multi-way if/else statements

• the conditional operator: an expression that exhibits the behavior of an if/else statement

• do/while: a loop that checks its condition after its body is executed

• for: a loop convenient for counting

These alternate constructs allow certain parts of algorithms to expressed more clearly and succinctly.
This chapter explores these other forms of expressing conditional execution and iteration.

7.1 The switch Statement

The switch statement provides a convenient alternative for some multi-way if/else statements like the
one in Listing 5.16 (restyleddigittoword.cpp). Listing 7.1 (switchdigittoword.cpp) is a new implementation
of Listing 5.16 (restyleddigittoword.cpp) that uses a switch statement instead of a multi-way if/else
statement.

Listing 7.1: switchdigittoword.cpp


#include <iostream>

int main() {
int value;
std::cout << "Please enter an integer in the range 0...5: ";
std::cin >> value;
switch (value) {
159
Fundamentals of C++ Programming
7.1. THE SWITCH STATEMENT 160

case 0:
std::cout << "zero";
break;
case 1:
std::cout << "one";
break;
case 2:
std::cout << "two";
break;
case 3:
std::cout << "three";
break;
case 4:
std::cout << "four";
break;
case 5:
std::cout << "five";
break;
default:
if (value < 0)
std::cout << "Too small";
else
std::cout << "Too large";
break;
}
std::cout << '\n';
}

The general form of a switch is:


160
Fundamentals of C++ Programming
7.1. THE SWITCH STATEMENT 161

switch ( integral expression ) {

case integral constant 1 :

statement sequence 1
break;

case integral constant 2 :

statement sequence 2
break;

case integral constant 3 :

statement sequence 3
break;

case integral constant n :

statement sequence n
break;

default:
default statement sequence
}

In a switch statement

• The reserved word switch identifies a switch statement.


• The required parenthesized expression that follows the word switch must evaluate to an integral
value. Any integer type, characters, and Boolean expressions are acceptable. Floating point expres-
sions and other non-integer types are forbidden.
161
Fundamentals of C++ Programming
7.1. THE SWITCH STATEMENT 162

• The body of the switch is enclosed by required curly braces.

• Each occurrence of the word case is followed by an integral constant and a colon (:). We call the
integral constant a case label. This label can be either a literal value or a const symbolic value
(see Section 3.6). In particular, non-const variables and other expressions are expressly forbidden.
The case label defines a position within the code; it is not an executable statement. A case label
represents a target to which the program’s execution flow can jump.
If the case label matches the switch’s expression, then the statements that follow that label are
executed up until the break statement is encountered. The statements and break statement that
follow each case label are optional. One way to execute one set of statements for more than one
case label is to provide empty statements for one or more of the labels, as in:
std::cin >> key; // get key from user
switch (key) {
case 'p':
case 'P':
std::cout << "You choose \"P\"\n";
break;
case 'q':
case 'Q':
done = true;
break;
}

Here either an upper- or lowercase P result in the same action— You chose P is printed. If the user
enters either an upper- or lowercase Q, the done Boolean variable is set to true. If the user enters
neither P nor Q, none of the statements in the switch is executed.
The break statement is optional. When a case label is matched, the statements that follow are ex-
ecuted until a break statement is encountered. The control flow then transfers out of the body of the
switch. In this way, the break within a switch works just like a break within a loop: the rest of
the body of the statement is skipped and program execution resumes at the next statement following
the body. A missing break statement, a common error, when its omission is not intentional, causes
the statements of the succeeding case label to be executed. The process continues until a break is
encountered or the end of the switch body is reached.

• The default label is matched if none of the case labels match. It serves as a catch all option like
the final else in a multi-way if/else statement. The default label is optional. If it is missing
and none of the case labels match the expression, then no statement within the switch’s body is
executed.

The switch statement has two restrictions that make it less general than the multi-way if/else:

• The switch argument must be an integral expression.

• Case labels must be constant integral values. Integral literals and constants are acceptable. Variables
or expressions are not allowed.

To illustrate these restrictions, consider the following if/else statement that translates easily to an equiv-
alent switch statement:
162
Fundamentals of C++ Programming
7.1. THE SWITCH STATEMENT 163

if (x == 1) {
// Do 1 stuff here . . .
}
else if (x == 2) {
// Do 2 stuff here . . .
}
else if (x == 3) {
// Do 3 stuff here . . .
}

The corresponding switch statement is:

switch (x) {
case 1:
// Do 1 stuff here . . .
break;
case 2:
// Do 2 stuff here . . .
break;
case 3:
// Do 3 stuff here . . .
break;
}

Now consider the following if/else:

if (x == y) {
// Do "y" stuff here . . .
}
else if (x > 2) {
// Do "> 2" stuff here . . .
}
else if (z == 3) {
// Do 3 stuff here . . .
}

This code cannot be easily translated into a switch statement. The variable y cannot be used as a case
label. The second choice checks for an inequality instead of an exact match, so direct translation to a case
label is impossible. In the last condition, a different variable is checked, z instead of x. The control flow
of a switch statement is determined by a single value (for example, the value of x), but a multi-way
if/else statement is not so constrained.
Where applicable, a switch statement allows programmers to compactly express multi-way selection
logic. Most programmers find a switch statement easier to read than an equivalent multi-way if/else
construct.
A positive consequence of the switch statement’s restrictions is that it allows the compiler to produce
more efficient code for a switch than for an equivalent if/else. If a choice must be made from one of
several or more options, and the switch statement can be used, then the switch statement will likely be
faster than the corresponding multi-way if/else.
163
Fundamentals of C++ Programming
7.2. THE CONDITIONAL OPERATOR 164

7.2 The Conditional Operator

As purely a syntactical convenience, C++ provides an alternative to the if/else construct called the
conditional operator. It has limited application but is convenient nonetheless. The following code fragment
assigns one of two things to x:

• the result of y/z, if z is nonzero, or


• zero, if z is zero; we wish to avoid the run-time error of division by zero.

// Assign a value to x:
if (z != 0)
x = y/z; // Division is possible
else
x = 0; // Assign a default value instead

This code has two assignment statements, but only one is executed at any given time. The conditional
operator makes for a more compact statement:
// Assign a value to x:
x = (z != 0) ? y/z : 0;

The general form of a conditional expression is:

( condition )? expression 1 : expression 2

• condition is a normal Boolean expression that might appear in an if statement. Parentheses around
the condition are not required but should be used to improve the readability.
• expression 1 is the overall value of the conditional expression if condition is true.
• expression 2 is the overall value of the conditional expression if condition is false.

The conditional operator uses two symbols (? and :) and three operands. Since it has three operands it
is classified as a ternary operator (C++’s only one). The overall type of a conditional expression is the more
dominant of exp1 and exp2 The conditional expression can be used anywhere an expression can be used. It
is not a statement itself; it is used within a statement.
As another example, the absolute value of a number is defined in mathematics by the following formula:

n, when n ≥ 0
|n| =
−n, when n < 0

In other words, the absolute value of a positive number or zero is the same as that number; the abso-
lute value of a negative number is the additive inverse (negative of) of that number. The following C++
expression represents the absolute value of the variable n:
164
Fundamentals of C++ Programming
7.3. THE DO/WHILE STATEMENT 165

(n < 0) ? -n : n

Some argue that the conditional operator is cryptic, and thus its use reduces a program’s readability. To
seasoned C++ programmers it is quite understandable, but it is used sparingly because of its very specific
nature.

7.3 The do/while Statement

An executing program checks the condition of a while statement (Section 6.1) before executing any of
the statements in its body; thus, we say a while loop is a top-checking loop. Sometimes this sequence
of checking the condition first then executing the body is inconvenient; for example, consider Listing 7.2
(goodinputonly.cpp).

Listing 7.2: goodinputonly.cpp


#include <iostream>

int main() {
int in_value = -1;
std::cout << "Please enter an integer in the range 0-10: ";
// Insist on values in the range 0...10
while (in_value < 0 || in_value > 10)
std::cin >> in_value;
// in_value at this point is guaranteed to be within range
std::cout << "Legal value entered was " << in_value << '\n';
}

The loop in Listing 7.2 (goodinputonly.cpp) traps the user in the while until the user provides a number
in the desired range. Here’s how it works:

• The condition of the while specifies a set that includes all values that are not in the desired range.
The initialization of in_value to −1 ensures the condition of the while will be true initially, and,
thus, the program always will execute the loop’s body at least one time.
• The user does not get a chance to enter a value until program’s execution is inside the loop.
• The only way the user can escape the loop is to enter a value that violates the condition—precisely a
value in the desired range.

The initialization of in_value before the loop check is somewhat artificial. It is there only to ensure
entry into the loop’s body. It seems unnatural to check for a valid value before the user gets a chance to
enter it. A loop that checks its condition after its body is executed at least once would be more appropri-
ate. The do/while statement is a bottom-checking loop that behaves exactly in this manner. Listing 7.3
(betterinputonly.cpp) uses a do/while statement to check for valid input.

Listing 7.3: betterinputonly.cpp


#include <iostream>

int main() {
int in_value;
std::cout << "Please enter an integer in the range 0-10: ";
165
Fundamentals of C++ Programming
7.3. THE DO/WHILE STATEMENT 166

Figure 7.1 The flowcharts for while and do/while loops

// Insist on values in the range 0...10


do
std::cin >> in_value;
while (in_value < 0 || in_value > 10);
// in_value at this point is guaranteed to be within range
std::cout << "Legal value entered was " << in_value << '\n';
}

Notice that there is no need to initialize in_value since its value is not used until after it is assigned
through the input stream std::cin. Figure 7.1 compares the flowcharts of a while and do/while
loop.
The do/while statement has the general form:

do

statement

while ( condition );

• The reserved words do and while identify a do/while statement. The do and while keywords
166
Fundamentals of C++ Programming
7.4. THE FOR STATEMENT 167

delimit the loop’s body, but curly braces are still required if the body consists of more than one
statement.

• The condition is associated with the while at the end of the loop. The condition is a Boolean
expression and must be enclosed within parentheses.

• The statement is exactly like the statement in the general form of the while loop (see Section 6.1).
It can be a compound statement enclosed within curly braces.

The body of a do/while statement, unlike the while statement, is guaranteed to execute at least
once.
The do/while loop is a convenience to the programmer and is not an essential programming construct.
It is easy to transform any code that uses a do/while statement into code that behaves identically that
uses a while statement instead. In practice, programmers use while loops much more frequently than
do/while loops because more algorithms require top-checking loops than bottom-checking loops. The
do/while statement is included in C++ for a reason, however. Transforming an algorithm that can be
expressed more naturally with a bottom-checking loop into one the uses a top-checking loop can lead to
awkward code. Use do/while when appropriate.

7.4 The for Statement

Recall Listing 6.2 (iterativecounttofive.cpp). It simply counts from one to five. Counting is a frequent activity
performed by computer programs. Certain program elements are required in order for any program to count:

• A variable must be used to keep track of the count; in Listing 6.2 (iterativecounttofive.cpp), count
is the aptly named counter variable.

• The counter variable must be given an initial value. In the case of Listing 6.2 (iterativecounttofive.cpp),
the initial value is 1.

• The variable must be modified (usually incremented) as the program counts. The statement
count++;

increments count in Listing 6.2 (iterativecounttofive.cpp).

• A way must be provided to determine if the counting has completed. In Listing 6.2 (iterativecounttofive.cpp),
the condition of the while statement determines if the counting is complete or must contine.

C++ provides a specialized loop that packages these four programming elements into one convenient
statement. Called the for statement, its general form is

for ( initialization ; condition ; modification )

statement

167
Fundamentals of C++ Programming
7.4. THE FOR STATEMENT 168

• The reserved word for identifies a for statement.


• The loop is controlled by a special variable called the loop variable.
• The header, contained in parentheses, contains three parts, each separated by semicolons:
– Initialization. The initialization part assigns an initial value to the loop variable. The loop
variable may be declared here as well; if it is declared here, then its scope is limited to the for
statement. This means you may use that loop variable only within the loop. It also means you
are free to reuse that variable’s name outside the loop to declare a different variable with the
same name as the loop variable.
The initialization part is performed one time.
– Condition. The condition part is a Boolean expression, just like the condition of a while
statement. The condition is checked each time before the body is executed.
– Modification. The modification part generally changes the loop variable. The change should be
such that the condition will eventually become false so the loop will terminate. The modification
is performed during each iteration after the body is executed.
Notice that the last part (modification) is not following by a semicolon; semicolons are used
strictly to separate the three parts.
• The statement is like the body of any other loop. It may be a compound statement within curly braces.

Any for loop can be rewritten as a while loop. The general form of the for loop given above can be
written equivalently as

initialization

while ( condition ) {

statement

modification

Listing 7.4 (forcounttofive.cpp) uses a for statement to count to five.

Listing 7.4: forcounttofive.cpp


#include <iostream>

int main() {
for (int count = 1; count <= 5; count++)
std::cout << count << '\n'; // Display counter
}
168
Fundamentals of C++ Programming
7.4. THE FOR STATEMENT 169

With a while loop, the four counting components (variable declaration, initialization, condition, and
modification can be scattered throughout the code. With a for loop, a programmer should be able to
determine all the important information about the loop’s control by looking at one statement.
Recall Listing 6.13 (timestable.cpp) that prints a multiplication table on the screen. We can organize
its code better by converting all the while statements to for statements. The result uses far less code, as
shown in Listing 7.5 (bettertimestable.cpp).

Listing 7.5: bettertimestable.cpp


#include <iostream>
#include <iomanip>

int main() {
int size; // The number of rows and columns in the table
std::cout << "Please enter the table size: ";
std::cin >> size;
// Print a size x size multiplication table

// First, print heading


std::cout << " ";
for (int column = 1; column <= size; column++)
std::cout << std::setw(4) << column; // Print heading for this column.
std::cout << '\n';
// Print line separator
std::cout << " +";
for (int column = 1; column <= size; column++)
std::cout << "----"; // Print separator for this column.
std::cout << '\n';
// Print table contents
for (int row = 1; row <= size; row++) {
std::cout << std::setw(4) << row << " |"; // Print row label.
for (int column = 1; column <= size; column++)
std::cout << std::setw(4) << row*column; // Display product
std::cout << '\n'; // Move cursor to next row
}
}

A for loop is ideal for stepping through the rows and columns. The information about the control of
both loops is now packaged in the respective for statements instead of being spread out in various places
in main. In the while version, it is easy for the programmer to forget to update one or both of the counter
variables (row and/or column). The for makes it harder for the programmer to forget the loop variable
update, since it is done right up front in the for statement header.
It is considered bad programming practice to do either of the following in a for statement:

• Modify the loop control variable within the body of the loop—if the loop variable is modified
within the body, then the logic of the loop’s control is no longer completely isolated to the for state-
ment’s header. The programmer must look elsewhere within the statement to understand completely
how the loop works.
• Prematurely exit the loop with a break—this action also violates the concept of keeping all the
loop control logic in one place (the for’s header).

The language allows both of these practices, but experience shows that it is best to avoid them. If it seems
169
Fundamentals of C++ Programming
7.4. THE FOR STATEMENT 170

necessary to violate this advice, consider using a different kind of loop. The while and do/while loops
do not imply the same degree of control regularity expected in a for loop.
Listing 7.6 (permuteabcd.cpp) is a rewrite of Listing 6.14 (permuteabc.cpp) that replaces its while
loops with for loops and adds an additional character.

Listing 7.6: permuteabcd.cpp


// File permuteabcd.cpp

#include <iostream>

int main() {
for (char first = 'A'; first <= 'D'; first ++)
for (char second = 'A'; second <= 'D'; second++)
if (second != first) // No duplicate letters
for (char third = 'A'; third <= 'D'; third++)
if (third != first && third != second)
for (char fourth = 'A'; fourth <= 'D'; fourth++)
if (fourth != first && fourth != second && fourth != third)
std::cout << first << second << third << fourth << '\n';
}

Notice that since all the variable initialization and incrementing is taken care of in the for statement
headers, we no longer need compound statements in the loop bodies, so the curly braces are unnecessary.
Listing 7.6 (permuteabcd.cpp) prints all 24 permutations of ABCD:
ABCD
ABDC
ACBD
ACDB
ADBC
ADCB
BACD
BADC
BCAD
BCDA
BDAC
BDCA
CABD
CADB
CBAD
CBDA
CDAB
CDBA
DABC
DACB
DBAC
DBCA
DCAB
DCBA

Listing 7.7 (forprintprimes.cpp) is a rewrite of Listing 6.22 (printprimes.cpp) that replaces its while
loops with for loops.

170
Fundamentals of C++ Programming
7.4. THE FOR STATEMENT 171

Listing 7.7: forprintprimes.cpp


#include <iostream>

int main() {

int max_value;
std::cout << "Display primes up to what value? ";
std::cin >> max_value;
for (int value = 2; value <= max_value; value++) {
// See if value is prime
bool is_prime = true; // Provisionally, value is prime
// Try all possible factors from 2 to value - 1
for (int trial_factor = 2;
is_prime && trial_factor < value;
trial_factor++)
is_prime = (value % trial_factor != 0);
if (is_prime)
std::cout << value << " "; // Display the prime number
}
std::cout << '\n'; // Move cursor down to next line
}

As shown in Listing 7.7 (forprintprimes.cpp), the conditional expression in the for loop is not limited
to a simple test of the loop control variable; it can be any legal Boolean expression. Programmers can use
the logical and (&&), or (||), and not (!) operators to create complex Boolean expressions, if necessary.
The modification part of the for loop is not limited to simple arithmetic and can be quite elaborate. For
example:
for (double d = 1000; d >= 1; std::cin >> d) {
/* Body goes here */
}

Here d is reassigned from the input stream. If necessary, multiple variables can be initialized in the initial-
ization part:
for (int i = 0, j = 100; i < j; i++) {
/* Body goes here */
}

While the for statement supports such complex headers, simpler is usually better. Ordinarily the for
loop should manage just one control variable, and the initialization, condition, and modification parts should
be straightforward. If a particular programming situation warrants an overly complicated for construction,
consider using another kind of loop.
Any or all of the parts of the for statement (initialization, condition, modification, and body) may be
omitted:

• Initialization. If the initialization is missing, as in


for (; i < 10; i++)
/* Body goes here */

then no initialization is performed by the for loop, and it must be done elsewhere.
171
Fundamentals of C++ Programming
7.4. THE FOR STATEMENT 172

• Condition. If the condition is missing, as in


for (int i = 0; ; i++)
/* Body goes here */

then the condition is true by default. A break or goto must appear in the body unless an infinite
loop is intended.

• Modification. If the modification is missing, as in


for (int i = 0; i < 10; )
/* Body goes here */

then the for performs no automatic modification; the modification must be done by a statement in
the body to avoid an infinite loop.

• Body. An empty body, as in


for (int i = 0; i < 10; i++) {}

or
for (int i = 0; i < 10; i++);

results in an empty loop. Some programmers use an empty loop to produce a non-portable delay in
the program’s execution. A programmer may, for example, need to slow down a graphical animation.
Such an attempt using an empty loop is non-portable for several reasons. If the program actually exe-
cutes the loop, slower computers will delay longer than faster computers. The timing of the program’s
delay will differ from one computer to another. Worse yet, some compilers may detect that such code
has no functional effect and optimize away the empty loop. This means the compiler will ignore the
for statement altogether.

As mentioned in Section 5.3, be careful about accidentally putting a semicolon at


the end of the for header, as in
for (int i = 0; i < 10; i++);
/* Intended body goes here */
The semicolon terminates the for statement, and the intended body that follows
is not the body, even though it may be properly indented.

One common C/C++ idiom to make an intentional infinite loop is to use a for statement with all control
information missing:
for ( ;; )
/* Body goes here */

Omitting all the parts of the for header is a statement from the programmer that says “I know what I am
doing—I really want an infinite loop here.” In reality the loop may not be infinite at all; its body could
contain a break or goto statement.
While the for statement supports the omission of parts of its header, such constructs should be avoided.
The intention of the for loop is to allow the programmer to see all the aspects of the loop’s control in one
172
Fundamentals of C++ Programming
7.5. EXERCISES 173

place. If some of these control responsibilities are to be handled elsewhere (not in the for’s header) then
consider using another kind of loop.
Programmers usually select a simple name for the control variable of a for statement. Recall that
variable names should be well chosen to reflect the meaning of their use within the program. It may come
as a surprise that i is probably the most common name used for an integer control variable in a for loop.
This practice has its roots in mathematics where variables such as i, j, and k are commonly used to index
vectors and matrices. Such mathematical structures have programming analogs in arrays and vectors,
which we explore in Chapter 11. Computer programmers make considerable use of for loops in array
and vector processing, so programmers have universally adopted this convention of short control variable
names. Thus, it generally is acceptable to use simple identifiers like i as loop control variables.
C++ allows the break, continue, and goto statements to be used in the body of a for statement.
Like with the while and do/while statements, break causes immediate loop termination, continue
causes the condition to be immediately checked to determine if the iteration should continue, and goto
jumps to a label somewhere in the function. As previously mentioned, however, for loop control should be
restricted to its header, and the use of break, continue, and goto within for loops should be avoided.
Any for loop can be rewritten with a while loop and behave identically. For example, consider the
for loop
for (int i = 1; i <= 10; i++)
std::cout << i << '\n';

and next consider the while loop that behaves exactly the same way:
int i = 1;
while (i <= 10) {
std::cout << i << '\n';
i++;
}

Which is better? The for loop conveniently packages the loop control information in its header, but in
the while loop this information is distributed throughout the small section of code. The for loop thus
provides a better organization of the loop control code. Does one loop outperform the other? No, most
compilers produce essentially the same code for both constructs. Thus, the for loop is preferred in this
example.

7.5 Exercises

1. Consider the following code fragment.


int x;
std::cin >> x;
switch (x + 3) {
case 5:
std::cout << x << '\n';
break;
case 10:
std::cout << x - 3 << '\n';
break;
case 20:
173
Fundamentals of C++ Programming
7.5. EXERCISES 174

std::cout << x + 3 << '\n';


break;
}

(a) What is printed when the user enters 2?


(b) What is printed when the user enters 5?
(c) What is printed when the user enters 7?
(d) What is printed when the user enters 17?
(e) What is printed when the user enters 20?

2. Consider the following code fragment.


char ch;
std::cin >> ch;
switch (ch) {
case 'a':
std::cout << "*\n";
break;
case 'A':
std::cout << "**\n";
break;
case 'B':
case 'b':
std::cout << "***\n";
case 'C':
case 'c':
std::cout << "****\n";
break;
default:
std::cout << "*****\n";
}

(a) What is printed when the user enters a?


(b) What is printed when the user enters A?
(c) What is printed when the user enters b?
(d) What is printed when the user enters B?
(e) What is printed when the user enters C?
(f) What is printed when the user enters c?
(g) What is printed when the user enters t?

3. What is printed by the following code fragment?


int x = 0;
do {
std::cout << x << " ";
x++;
} while (x < 10);
std::cout << '\n';

174
Fundamentals of C++ Programming
7.5. EXERCISES 175

4. What is printed by the following code fragment?


int x = 20;
do {
std::cout << x << " ";
x++;
} while (x < 10);
std::cout << '\n';

5. What is printed by the following code fragment?


for (int x = 0; x < 10; x++)
std::cout << "*";
std::cout << '\n';

6. Rewrite the following code fragment so that a switch is used instead of the if/else statements.
int value;
char ch;
std::cin >> ch;
if (ch == 'A')
value = 10;
else if (ch == 'P')
value = 20;
else if (ch == 'T')
value = 30;
else if (ch == 'V')
value = 40;
else
value = 50;
std::cout << value << '\n';

7. Rewrite the following code fragment so that a multi-way if/else is used instead of the switch
statement.
int value;
char ch;
std::cin >> ch;
switch( ch) {
case 'A':
value = 10;
break;
case 'P':
std::cin >> value;
break;
case 'T':
value = ch;
break;
case 'V':
value = ch + 1000;
break;
default:
175
Fundamentals of C++ Programming
7.5. EXERCISES 176

value = 50;
}
std::cout << value << '\n';

8. Rewrite the following code fragment so that a multi-way if/else is used instead of the switch
statement.
int value;
char ch;
std::cin >> ch;
switch (ch) {
case 'A':
std::cout << ch << '\n';
value = 10;
break;
case 'P':
case 'E':
std::cin >> value;
break;
case 'T':
std::cin >> ch;
value = ch;
case 'C':
value = ch;
std::cout << "value=" << value << ", ch=" << ch << '\n';
break;
case 'V':
value = ch + 1000;
break;
}
std::cout << value << '\n';

9. Rewrite the following code fragment so a while loop is used instead of the for statement.
for (int i = 100; i > 0; i--)
std::cout << i << '\n';

10. Rewrite the following code fragment so that it uses the conditional operator instead of an if state-
ment:
if (value % 2 != 0) // Is value even?
value = value + 1; // If not, make it even.

11. Rewrite the following code fragment so that it uses the conditional operator instead of an if/else
statement:
if (value % 2 == 0) // Is value even?
value = 0; // If so, make it zero.
else
value = value + 1; // Otherwise, make it even.

176
Fundamentals of C++ Programming
7.5. EXERCISES 177

12. Would the following multi-way if/else be a good candidate to rewrite as a switch statement? If
so, rewrite the code using a switch; otherwise, explain why it is impractical to do so.
int x, y;
std::cin >> x >> y;
if (x < 10)
y = 10;
else if (x == 5)
y = 5;
else if (x == y)
y = 0;
else if (y > 10)
x = 10;
else
x = y;

177
Fundamentals of C++ Programming
7.5. EXERCISES 178

178
Fundamentals of C++ Programming
179

Chapter 8

Using Functions

Suppose you must write a C++ program that computes the square root of a number supplied by the user.
Listing 8.1 (computesquareroot.cpp) provides a simple implementation.

Listing 8.1: computesquareroot.cpp


// File squareroot.cpp

#include <iostream>

int main() {
double input;

// Get value from the user


std::cout << "Enter number: ";
std::cin >> input;
double diff;
// Compute a provisional square root
double root = 1.0;

do { // Loop until the provisional root


// is close enough to the actual root
root = (root + input/root) / 2.0;
std::cout << "root is " << root << '\n';
// How bad is the approximation?
diff = root * root - input;
}
while (diff > 0.0001 || diff < -0.0001);

// Report approximate square root


std::cout << "Square root of " << input << " = " << root << '\n';
}

The program is based on a simple algorithm, Newton’s Method, that uses successive approximations to
zero in on an answer that is within 0.0001 of the true answer.
One sample run is
Enter number: 2
179
Fundamentals of C++ Programming
Using Functions 180

root is 1.5
root is 1.41667
root is 1.41422
Square root of 2 = 1.41422

The actual square root is approximately 1.4142135623730951 and so the result is within our accepted
tolerance (0.0001). Another run is

Enter number: 100


root is 50.5
root is 26.2401
root is 15.0255
root is 10.8404
root is 10.0326
root is 10.0001
root is 10
Square root of 100 = 10

which is, of course, the exact answer.


While this code may be acceptable for many applications, better algorithms exist that work faster and
produce more precise answers. Another problem with the code is this: What if you are working on a sig-
nificant scientific or engineering application and must use different formulas in various parts of the source
code, and each of these formulas involve square roots in some way? In mathematics, for example, you use
square root to compute the distance between two geometric points (x1 , y1 ) and (x2 , y2 ) as
q
(x2 − x1 )2 + (y2 − y1 )2

and, using the quadratic formula, the solution to the equation ax2 + bx + c = 0 is

−b ± b2 − 4ac
2a

In electrical engineering and physics, the root mean square of a set of values {a1 , a2 , a3 , . . . , an } is
s
a21 + a22 + a23 + . . . + a2n
n

Suppose we are writing one big program that, among many other things, needs to compute distances and
solve quadratic equations. Must we copy and paste the relevant portions of the square root code in List-
ing 8.1 (computesquareroot.cpp) to each location in our source code that requires a square root compu-
tation? Also, what if we develop another program that requires computing a root mean square? Will we
need to copy the code from Listing 8.1 (computesquareroot.cpp) into every program that needs to compute
square roots, or is there a better way to package the square root code and reuse it?
Code is made reusable by packaging it in functions. A function is a unit of reusable code. In Chapter 9
we will write our own reusable functions, but in this chapter we examine some of the functions available
in the C++ standard library. C++ provides a collection of standard precompiled C and C++ code stored
in libraries. Programmers can use parts of this library code within their own code to build sophisticated
programs.
180
Fundamentals of C++ Programming
8.1. INTRODUCTION TO USING FUNCTIONS 181

Figure 8.1 Conceptual view of the square root function

8.1 Introduction to Using Functions

In mathematics, a function computes a result from a given value; for example, from the function definition
f (x) = 2x + 3, we can compute f (5) = 13 and f (0) = 3. A function in C++ works like a mathematical
function. To introduce the function concept, we will look at the standard C++ function that implements
mathematical square root.
In C++, a function is a named sequence of code that performs a specific task. A program itself consists
of a collection of functions. One example of a function is the mathematical square root function. Such a
function, named sqrt, is available to C and C++ programs (see Section 8.2). The square root function
accepts one numeric value and produces a double value as a result; for example, the square root of 16 is
4, so when presented with 16.0, sqrt responds with 4.0. Figure 8.1 visualizes the square root function.
For the programmer using the sqrt function within a program, the function is a black box; the pro-
grammer is concerned more about what the function does, not how it does it.
This sqrt function is exactly what we need for our square root program, Listing 8.1 (computesquareroot.cpp).
The new version, Listing 8.2 (standardsquareroot.cpp), uses the library function sqrt and eliminates the
complex logic of the original code.

Listing 8.2: standardsquareroot.cpp


#include <iostream>
#include <cmath>

int main() {
double input;

// Get value from the user


std::cout << "Enter number: ";
std::cin >> input;

// Compute the square root


double root = sqrt(input);

// Report result
std::cout << "Square root of " << input << " = " << root << '\n';
}

The line
#include <cmath>
181
Fundamentals of C++ Programming
8.1. INTRODUCTION TO USING FUNCTIONS 182

directs the preprocessor to augment our source code with the declarations of a collection of mathematical
functions in the cmath library. The sqrt function is among them. Table 8.1 lists some of the other com-
monly used mathematical functions available in the cmath library. The compiler needs this augmented code
so it can check to see if we are using the sqrt function properly.
The expression
sqrt(input)

is a function invocation, also known as a function call. A function provides a service to the code that uses
it. Here, our main function is the caller1 that uses the service provided by the sqrt function. We say
main calls, or invokes, sqrt passing it the value of input. The expression sqrt(input) evaluates to
the square root of the value of the variable input. Behind the scenes—inside the black box as it were—
precompiled C code uses the value of the input variable to compute its square root. There is nothing
special about this precompiled C code that constitutes the sqrt function; it was written by a programmer
or team of programmers working for the library vendor using the same tools we have at our disposal. In
In Chapter 9 we will write our own functions, but for now we will enjoy the functions that others have
provided for us.
When calling a function, a pair of parentheses follow the function’s name. Information that the function
requires to perform its task must appear within these parentheses. In the expression
sqrt(input)

input is the information the function needs to do its work. We say input is the argument, or parameter,
passed to the function. We also can say “we are passing input to the sqrt function.”
While we might say “we are passing input to the sqrt function,” the program really is not giving the
function access to main’s input variable. The sqrt function itself cannot change the value of main’s
input variable, it simply uses the variable’s value to perform the computation.
The following simple analogy may help explain how the communication works between main and
sqrt, The main function has work to do, but instead of doing all the work itself, it delegates some of the
work (in this case the hard part) to sqrt. When main needs to compute the square root of input, it writes
down the value of its input variable on a piece of paper and hands it to sqrt. main then sits idly until
sqrt finishes its work. The sqrt function accepts main’s note and begins working on the task (computing
the square root of the number on the note main gave it). When it is finished, sqrt does two things: sqrt
hands back to main a different piece of paper with the answer, and sqrt throws away the piece of paper
main originally passed to it. When main receives the note from sqrt it uses the information on the note
and then discards the note. The main function then can continue with its other business.
The sqrt function thus has no access to main’s original input variable; it has only a copy of input,
as if “written on a piece of paper.” (Similarly, if the sqrt function uses any variables to do its work, main is
oblivious to them and has no way to access them.) After sqrt is finished and returns to main its computed
answer, sqrt discards its copy of input (by analogy, the function “throws away” the paper with the copy
of input that main gave it). Thus, during a function call the parameter is a temporary, transitory value
used only to communicate information to the function. The parameter lives only as long as the function is
executing.
Figure 8.2 illustrates a program’s execution involving simple function calls.
Figure 8.2 shows that a program’s execution begins in its main function. Here main calls the sqrt
function twice. A vertical bar represents the time that a function is active, or “alive.” A function’s variables
1 The term client can be used as well, although we reserve the term client for code that interacts with objects (see Chapter 13).
182
Fundamentals of C++ Programming
8.1. INTRODUCTION TO USING FUNCTIONS 183

Figure 8.2 The diagram on the right visualizes the execution of the program on the left. Time flows from
left to right. A rectangular bar represents the time that a function is active. A C++ program’s execution
begins with its main function. Here, main calls the sqrt function twice. The shaded parts of main’s bar
shows the times main has to wait for sqrt to complete.

int main() {
double value; Program Execution

// Assign variable Time


value = 16;
main
// Compute s square root
double root = sqrt(value); 16 100
4 10
// Compute another
root = sqrt(100); sqrt sqrt
}

exist while a function is active. Observe that the main function is active for the duration of the program’s
execution. The sqrt becomes active twice, exactly the two times main calls it.
The sqrt function can be called in other ways, as Listing 8.3 (usingsqrt.cpp) illustrates:

Listing 8.3: usingsqrt.cpp


/*
* This program shows the various ways the
* sqrt function can be used.
*/

#include <iostream>
#include <cmath>

int main() {
double x = 16.0;
// Pass a literal value and display the result
std::cout << sqrt(16.0) << '\n';
// Pass a variable and display the result
std::cout << sqrt(x) << '\n';
// Pass an expression
std::cout << sqrt(2 * x - 5) << '\n';
// Assign result to variable
double y = sqrt(x);
// Use result in an expression
y = 2 * sqrt(x + 16) - 4;
// Use result as argument to a function call
y = sqrt(sqrt(256.0));
std::cout << y << '\n';
}

183
Fundamentals of C++ Programming
8.1. INTRODUCTION TO USING FUNCTIONS 184

Figure 8.3 Conceptual view of the maximum function

The sqrt function accepts a single numeric argument. The parameter that a caller can pass to sqrt
can be a literal number, a numeric variable, an arithmetic expression, or even a function invocation that
produces an acceptable numeric result.
Some C++ functions, like sqrt, compute a value and return it to the caller. The caller can use this
result in various ways, as shown in Listing 8.3 (usingsqrt.cpp
p√ ). The next to the last statement passes the
result of calling sqrt to sqrt, thereby computing 256, which is 4.
If the caller code attempts to pass a parameter to the function that is incompatible with the type expected
by the function, the compiler will issue an error.
std::cout << sqrt("16") << '\n'; // Illegal, a string is not a number

The compiler is able to determine that the above statement is illegal based on the additional information the
preprocessor added to the code via the
#include<cmath>

directive.
Listing 8.3 (usingsqrt.cpp) shows that a program can call the sqrt function as many times and in as
many places as needed. As noted in Figure 8.1, to the caller of the square root function, the function is a
black box; the caller is concerned strictly about what the function does, not how the function accomplishes
its task.
We safely can treat all functions as black boxes. We can use the service that a function provides without
being concerned about its internal details. We are guaranteed that we can influence the function’s behavior
only via the parameters that we pass, and that nothing else we do can affect what the function does or how
it does it. Furthermore, the function cannot affect any of our code, apart from what we do with the value it
computes.
Some functions take more than one parameter; for example, the C++ max function requires two argu-
ments in order to produce a result. The max function selects and returns the larger of the two parameters.
The max function is visualized in Figure 8.3.
The max function could be used as
std::cout << "The larger of " << 4 << " and " << 7
<< " is " << max(4, 7) << '\n';

Notice that the parameters are contained in parentheses following the function’s name, and the parameters
are separated by commas.
From the caller’s perspective a function has three important parts:
184
Fundamentals of C++ Programming
8.1. INTRODUCTION TO USING FUNCTIONS 185

• Name. Every function has a name that identifies the location of the code to be executed. Function
names follow the same rules as variable names; a function name is another example of an identifier
(see Section 3.3).

• Parameter type(s). A caller must provide the exact number and types of parameters that a function
expects. If a caller attempts to call a function with too many or too few parameters, the compiler will
issue an error message and not compile the code. Similarly, if the caller passes parameters that are
not compatible with the types specified for the function, the compiler will report appropriate error
messages.

• Result type. A function can compute a result and return this value to the caller. The caller’s use of
this result must be compatible with the function’s specified result type. The result type returned to the
caller and the parameter types passed in by the caller can be completely unrelated.

These three crucial pieces of information are formally described for each function in a specification
known as a function prototype. The prototype for the sqrt function is
double sqrt(double)

and the max function’s prototype can be expressed as 2


int max(int, int)

In a function prototype, the return type is listed first, followed by the function’s name, and then the parameter
types appear in parentheses. Sometimes it is useful to list parameter names in the function’s prototype, as
in
double sqrt(double n)

or
int max(int a, int b)

The specific parameter names are irrelevant. The names make it easier to describe what the function does;
for example, sqrtcomputes the square root of n and max determines the larger of a and b.
When using a library function the programmer must include the appropriate #include directive in
the source code. The file specified in an #include directive contains prototypes for library functions. In
order to use the sqrt function, a program must include the
#include <cmath>

preprocessor directive. For the max function, include


#include <algorithm>

although under Visual C++, including the <iostream> header is sufficient.


Armed with the function prototypes, the compiler can check to see if the calling code is using the library
functions correctly. An attempt to use the sqrt function as
std::cout << sqrt(4.0, 7.0) << '\n'; // Error
2 The prototype for the actual library function max uses generic types; generic types are beyond the scope of this introductory book,

so the prototype provided here is strictly for illustrating a point.


185
Fundamentals of C++ Programming
8.2. STANDARD MATH FUNCTIONS 186

will result in an error because the prototype for sqrt specifies only one numeric parameter, not two.
Some functions do not accept parameters; for example, the C++ function to generate a pseudorandom
number, rand, is called with no arguments:

std::cout << rand() << '\n';

The rand function returns an int value, but the caller does not pass the function any information to do its
task. The rand prototype is

int rand()

Notice the empty parentheses that indicate this function does not accept any parameters.
Unlike mathematical functions that must produce a result, C++ does not require a function to return a
value to its caller. The C++ function exit expects an integer value from the caller, but it does not return a
result back to the caller. A prototype for a function that returns nothing uses void as the return type, as in:

void exit(int);

The exit function immediately terminates the program’s execution. The integer argument passed to exit
is returned to the operating system which can use the value to determine if the program terminated normally
or due to an error. C++ programs automatically return zero when main finishes executing—no exit call
is necessary.
Note that since exit does not return a value to the caller, code such as

std::cout << exit(8) << '\n'; // Illegal!

will not compile since the expression exit(8) evaluates to nothing, and the std::cout stream object
requires an actual value of some kind to print. A void function is useful for the side effects it produces
instead a value it computes. Example side effects include printing something on the console, sending data
over a network, or animating a graphical image.

8.2 Standard Math Functions

The cmath library provides much of the functionality of a scientific calculator. Table 8.1 lists only a few
of the available functions.
186
Fundamentals of C++ Programming
8.2. STANDARD MATH FUNCTIONS 187

mathfunctions Module
double sqrt(double x) √
Computes the square root of a number: sqrt(x) = x
double exp(double x)
Computes e raised a power: exp(x) = ex
double log(double x)
Computes the natural logarithm of a number: log(x) = loge x = ln x
double log10(double x)
Computes the common logarithm of a number: log(x) = log10 x
double cos(double)
Computes the cosine of a value specified in radians: cos(x) = cos x; other trigonometric
functions include sine, tangent, arc cosine, arc sine, arc tangent, hyperbolic cosine, hyper-
bolic sine, and hyperbolic tangent
double pow(double x, double y)
Raises one number to a power of another: pow(x, y) = xy
double fabs(double x)
Computes the absolute value of a number: fabs(x) = |x|

Table 8.1: A few of the functions from the cmath library

The cmath library also defines a constant named HUGE_VAL. Programmers can use this constant to
represent infinity or an undefined value such the slope of a vertical line or a fraction with a zero denominator.
A complete list of the numeric functions available to C++ can be found at http://www.cplusplus.
com/reference/clibrary/cmath/.

Be careful to put the function’s arguments in the proper order when calling a
function; for example, the call pow(10,2) computes 102 = 100, but the call
pow(2,10) computes 210 = 1, 024.

A C++ program that uses any of the functions from the cmath library must use the following prepro-
cessor #include directive:
#include <cmath>

Functions in the cmath library are ideal for solving problems like the one shown in Figure 8.4. Suppose
a spacecraft is at a fixed location in space relative to some planet. The spacecraft’s distance to the planet,
therefore, also is fixed. A satellite is orbiting the planet in a circular orbit. We wish to compute how much
farther away the satellite will be from the spacecraft when it has progressed 10 degrees along its orbital
path.
We will let the origin of our coordinate system (0,0) be located at the center of the planet which cor-
responds also to the center of the circular orbital path. The satellite is initially at point (x1 , y1 ) and the
spacecraft is stationary at point (px , py ). The spacecraft is located in the same plane as the satellite’s orbit.
We need to compute the difference in the distances between the moving point (satellite) and the fixed point
(spacecraft) at two different times during the satellite’s orbit.
Facts from mathematics provide solutions to the following two problems:
187
Fundamentals of C++ Programming
8.2. STANDARD MATH FUNCTIONS 188

Figure 8.4 Orbital distance problem. In this diagram, the satellite begins at point (x1 , y1 ), a distance of d1
from the spacecraft. The satellite’s orbit takes it to point (x2 , y2 ) after an angle of θ rotation. The distance
to its new location is d2 .

(x2,y2)

d2
θ (px,py)
(x1,y1) d1
(0,0)

1. Problem: We must recompute the location of the moving point as it moves along the circle.
Solution: Given an initial position (x1 , y1 ) of the moving point, a rotation of θ degrees around the
origin will yield a new point at (x2 , y2 ), where

x2 = x1 cos θ − y1 sin θ
y2 = x1 sin θ + y1 cos θ

2. Problem: The distance between the moving point and the fixed point must be recalculated as the
moving point moves to a new position.
Solution: The distance d1 in Figure 8.4 between two points (px , py ) and (x1 , y1 ) is given by the
formula q
d1 = (x1 − px )2 + (y1 − py )2

Similarly, the distance d2 in Figure 8.4 is


q
d2 = (x2 − px )2 + (y2 − py )2

Listing 8.4 (orbitdist.cpp) uses these mathematical results to compute the difference in the distances.

Listing 8.4: orbitdist.cpp


#include <iostream>
#include <cmath>

int main() {
// Location of orbiting point is (x,y)
double x; // These values change as the
188
Fundamentals of C++ Programming
8.2. STANDARD MATH FUNCTIONS 189

double y; // satellite moves


const double PI = 3.14159;

// Location of fixed point is always (100, 0),


// AKA (p_x, p_y). Change these as necessary.
const double p_x = 100;
const double p_y = 0;

// Radians in 10 degrees
const double radians = 10 * PI/180;

// Precompute the cosine and sine of 10 degrees


const double COS10 = cos(radians);
const double SIN10 = sin(radians);

// Get starting point from user


std::cout << "Enter initial satellite coordinates (x,y):";
std::cin >> x >> y;

// Compute the initial distance


double d1 = sqrt((p_x - x)*(p_x - x) + (p_y - y)*(p_y - y));

// Let the satellite orbit 10 degrees


double x_old = x; // Remember x's original value
x = x*COS10 - y*SIN10; // Compute new x value
// x's value has changed, but y's calculate depends on
// x's original value, so use x_old instead of x.
y = x_old*SIN10 + y*COS10;

// Compute the new distance


double d2 = sqrt((p_x - x)*(p_x - x) + (p_y - y)*(p_y - y));

// Print the difference in the distances


std::cout << "Difference in distances: " << d2 - d1 << '\n';
}

We can use the square root function to improve the efficiency of our primes program. Instead of trying
all the factors of n up to n − 1, we need only try potential factors up to the square root of n. Listing 8.5
(moreefficientprimes.cpp) uses the sqrt function to reduce the number of factors that need be considered.

Listing 8.5: moreefficientprimes.cpp


#include <iostream>
#include <cmath>

int main() {

int max_value;
std::cout << "Display primes up to what value? ";
std::cin >> max_value;
for (int value = 2; value <= max_value; value++) {
// See if value is prime
bool is_prime = true; // Provisionally, value is prime
double r = value, root = sqrt(r);
// Try all possible factors from 2 to the square
189
Fundamentals of C++ Programming
8.3. MAXIMUM AND MINIMUM 190

// root of value
for (int trial_factor = 2;
is_prime && trial_factor <= root; trial_factor++)
is_prime = (value % trial_factor != 0);
if (is_prime)
std::cout << value << " "; // Display the prime number
}
std::cout << '\n'; // Move cursor down to next line
}

The sqrt function comes in three forms:

double sqrt(double)
float sqrt(float)
long double sqrt(long double)

The function names are the same, but the parameter types differ. We say that the sqrt function is over-
loaded. (Overloaded functions are covered in more detail in Section 10.3.) When a caller invokes the sqrt
function, the compiler matches the call to the closest matching prototype. If the caller passes a double
parameter, the compiler generates code to call the double version. If the caller instead passes a float
variable, the compiler selects the float version of sqrt. When an int is passed to sqrt, the compiler
cannot decide which version to use, because an int can be converted automatically to either a float,
double, or long double. The compiler thus needs some help to resolve the ambiguity, so we intro-
duced an additional variable of type double so the compiler will use the double version of the sqrt
function. Another option is to use a type cast to convert the integer value into one of the types acceptable to
the sqrt function.

8.3 Maximum and Minimum

C++ provides standard functions for determining the maximum and minimum of two numbers. Listing 8.6
(maxmin.cpp) exercises the standard min and max functions.

Listing 8.6: maxmin.cpp


#include <iostream>
#include <algorithm>

int main() {
int value1, value2;
std::cout << "Please enter two integer values: ";
std::cin >> value1 >> value2;
std::cout << "max = " << std::max(value1, value2)
<< ", min = " << std::min(value1, value2) << '\n';
}

To use the standard max and min functions in a program you must include the <algorithm> header.
190
Fundamentals of C++ Programming
8.4. CLOCK FUNCTION 191

8.4 clock Function

The clock function from the <ctime> library requests from the operating system the amount of time an
executing program has been running. The units returned by the call clock() is system dependent, but it
can be converted into seconds with the constant CLOCKS_PER_SEC, also defined in the ctime library.
Under Visual C++, the CLOCKS_PER_SEC constant is 1,000, which means the call clock() returns the
number of milliseconds that the program has been running.
Using two calls to the clock function you can measure elapsed time. Listing 8.7 (timeit.cpp) measures
how long it takes a user to enter a character from the keyboard.

Listing 8.7: timeit.cpp


#include <iostream>
#include <ctime>

int main() {
char letter;
std::cout << "Enter a character: ";
clock_t seconds = clock(); // Record starting time
std::cin >> letter;
clock_t other = clock(); // Record ending time
std::cout << static_cast<double>(other - seconds)/CLOCKS_PER_SEC
<< " seconds\n";
}

The type clock_t is a type defined in the <ctime> header file. clock_t is equivalent to an
unsigned long, and you can perform arithmetic on clock_t values and variables just as if they are
unsigned longs. In the expression
static_cast<double>(other - seconds)/CLOCKS_PER_SEC

the cast is required to force floating-point division; otherwise, the result is truncated to an integer value.
As a more practical example, Listing 8.8 (measureprimespeed.cpp) measures how long it takes a pro-
gram to display all the prime numbers up to half a million using the algorithm from Listing 7.7 (forprintprimes.cpp).

Listing 8.8: measureprimespeed.cpp


#include <iostream>
#include <ctime>
#include <cmath>

// Display the prime numbers between 2 and 500,000 and


// time how long it takes

int main() {
clock_t start_time = clock(), // Record start time
end_time;
for (int value = 2; value <= 500000; value++) {
// See if value is prime
bool is_prime = true; // Provisionally, value is prime
// Try all possible factors from 2 to n - 1
for (int trial_factor = 2;
is_prime && trial_factor < value;
191
Fundamentals of C++ Programming
8.5. CHARACTER FUNCTIONS 192

trial_factor++)
is_prime = (value % trial_factor != 0);
if (is_prime)
std::cout << value << " "; // Display the prime number
}
std::cout << '\n'; // Move cursor down to next line
end_time = clock();
// Print the elapsed time
std::cout << "Elapsed time: "
<< static_cast<double>(end_time - start_time)/CLOCKS_PER_SEC
<< " sec." << '\n';
}

On one system, the program took 93 seconds, on average, to print all the prime numbers up to 500,000.
By comparison, the newer, more efficient version, Listing 8.5 (moreefficientprimes.cpp), which uses the
square root optimization takes only 15 seconds to display all the primes up to 500,000. Exact times will
vary depending on the speed of the computer.
As it turns out, much of the program’s execution time is taken up printing the output, not computing the
prime numbers to print. We can compare the algorithms better by redirecting the program’s output to a file.
If the executable program is named primes.exe, you can redirect its output at the command line by issuing
the command
primes > run1.out

This creates a text file named run1.out that can be viewed with any text editor. Its contents are exactly what
would have been printed to the screen if the redirection is not used.
When run using redirection, the time difference is even more dramatic: The unoptimized version gen-
erates the prime numbers up to 500,000 in 77 seconds, while the optimized square root version requires
only 2 seconds to generate the same number of primes! An even faster prime generator can be found in
Listing 11.26 (fasterprimes.cpp); it uses a completely different algorithm to generate prime numbers.
You must #include the <ctime> header to use the standard time function in a program.

8.5 Character Functions

The C library provides a number of character functions that are useful to C++ programmers. Listing 8.9
(touppercase.cpp) converts lowercase letters to uppercase letters.

Listing 8.9: touppercase.cpp


#include <iostream>
#include <cctype>

int main() {
for (char lower = 'a'; lower <= 'z'; lower++) {
char upper = toupper(lower);
std::cout << lower << " => " << upper << '\n';
}
}

The first lines printed by Listing 8.9 (touppercase.cpp) are


192
Fundamentals of C++ Programming
8.6. RANDOM NUMBERS 193

a => A
b => B
c => C
d => D

Interestingly, the toupper function returns an int, not a char. At the enhanced warning level 4 for
Visual C++ a cast is required to assign the result to the variable upper:
char upper = static_cast<char>(toupper(lower));

Some of the more useful character functions are described in Table 8.2.

charfunctions Module
int toupper(int ch)
Returns the uppercase version of the given character; returns the original character if no
uppercase version exists (such as for punctuation or digits)
int tolower(int ch)
Returns the lowercase version of the given character; returns the original character if no
lowercase version exists (such as for punctuation or digits)
int isupper(int ch)
Returns a nonzero value (true) if ch is an uppercase letter (’A’–’Z’); otherwise, it returns
0 (false)
int islower(int ch)
Returns a nonzero value (true) if ch is an lowercase letter (’a’–’z’); otherwise, it returns
0 (false)
int isalpha(int ch)
Returns a nonzero value (true) if ch is a letter from the alphabet (’A’–’Z’ or ’a’–’z’);
otherwise, it returns 0 (false)
int isdigit(int ch)
Returns a nonzero value (true) if ch is a digit (’0’–’9’); otherwise, it returns 0 (false)

Table 8.2: A few of the functions from the cctype library

Other functions exist to determine if a character is a punctuation character like a comma or semicolon
(ispunct), a space, tab, or newline character (isspace).
To use the standard C character functions in your C++ program, you must include the <cctype>
header file.

8.6 Random Numbers

Some applications require behavior that appears random. Random numbers are useful particularly in games
and simulations. For example, many board games use a die (one of a pair of dice) to determine how many
places a player is to advance. (See Figure 8.5.) A die or pair of dice are used in other games of chance. A
die is a cube containing spots on each of its six faces. The number of spots range from one to six. A player
rolls a die or sometimes a pair of dice, and the side(s) that face up have meaning in the game being played.
The value of a face after a roll is determined at random by the complex tumbling of the die. A software
adaptation of a game that involves dice would need a way to simulate the random roll of a die.
193
Fundamentals of C++ Programming
8.6. RANDOM NUMBERS 194

Figure 8.5 A pair of dice

All algorithmic random number generators actually produce pseudorandom numbers, not true random
numbers. A pseudorandom number generator has a particular period, based on the nature of the algorithm
used. If the generator is used long enough, the pattern of numbers produced repeats itself exactly. A se-
quence of true random numbers would not contain such a repeating subsequence. The good news is that
all practical algorithmic pseudorandom number generators have periods that are large enough for most
applications.
C++ programmers can use two standard C functions for generating pseudorandom numbers: srand
and rand:
void srand(unsigned)
int rand()

srand establishes the first value in the sequence of pseudorandom integer values. Each call to rand
returns the next value in the sequence of pseudorandom values. Listing 8.10 (simplerandom.cpp) shows
how a sequence of 100 pseudorandom numbers can be printed.

Listing 8.10: simplerandom.cpp


#include <iostream>
#include <cstdlib>

int main() {
srand(23);
for (int i = 0; i < 100; i++) {
int r = rand();
std::cout << r << " ";

}
std::cout << '\n';
}

The numbers printed by the program appear to be random. The algorithm is given a seed value to begin,
and a formula is used to produce the next value. The seed value determines the sequence of numbers gener-
ated; identical seed values generate identical sequences. If you run the program again, the same sequence is
displayed because the same seed value, 23, is used. In order to allow each program run to display different
sequences, the seed value must be different for each run. How can we establish a different seed value for
each run? The best way to make up a “random” seed at run time is to use the time function which is found
in the ctime library. The call time(0) returns the number of seconds since midnight January 1, 1970.
194
Fundamentals of C++ Programming
8.6. RANDOM NUMBERS 195

This value obviously differs between program runs, so each execution will use a different seed value, and the
generated pseudorandom number sequences will be different. Listing 8.11 (betterrandom.cpp) incorporates
the time function to improve its randomness over multiple executions.

Listing 8.11: betterrandom.cpp


#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
srand(static_cast<unsigned>(time(0)));
for (int i = 0; i < 100; i++) {
int r = rand();
std::cout << r << " ";

}
std::cout << '\n';
}

Each execution of Listing 8.11 (betterrandom.cpp) produces a different pseudorandom number se-
quence. The actual type of value that time returns is time_t, so the result from a call to time must
be cast to unsigned int before being used with srand.
Notice that the numbers returned by rand can be rather large. The pseudorandom values range from 0
to a maximum value that is implementation dependent. The maximum value for Visual C++ś rand function
is 32,767, which corresponds to the largest 16-bit signed int value. The cstdlib header defines the
constant RAND_MAX that represents the largest value in the range. The following statement
std::cout << RAND_MAX << '\n';

would print the value of RAND_MAX for a particular system.


Ordinarily we need values in a more limited range, like 1...100. Simple arithmetic with the modulus
operator can produce the result we need. If n is any nonnegative integer and m is any positive integer, the
expression

n%m

produces a value in the range 0 . . . m − 1.


This means the statement
int r = rand() % 100;

can assign only values in the range 0...99 to r. If we really want values in the range 1...100, what can we
do? We simply need only add one to the result:
int r = rand() % 100 + 1;

This statement produces pseudorandom numbers in the range 1...100.


We now have all we need to write a program that simulates the rolling of a die.
Listing 8.12 (die.cpp) simulates rolling die.
195
Fundamentals of C++ Programming
8.6. RANDOM NUMBERS 196

Listing 8.12: die.cpp


#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
// Set the random seed value
srand(static_cast<unsigned>(time(0)));

// Roll the die three times


for (int i = 0; i < 3; i++) {
// Generate random number in the range 1...6
int value = rand() % 6 + 1;

// Show the die


std::cout << "+-------+\n";
switch (value) {
case 1:
std::cout << "| |\n";
std::cout << "| * |\n";
std::cout << "| |\n";
break;
case 2:
std::cout << "| * |\n";
std::cout << "| |\n";
std::cout << "| * |\n";
break;
case 3:
std::cout << "| * |\n";
std::cout << "| * |\n";
std::cout << "| * |\n";
break;
case 4:
std::cout << "| * * |\n";
std::cout << "| |\n";
std::cout << "| * * |\n";
break;
case 5:
std::cout << "| * * |\n";
std::cout << "| * |\n";
std::cout << "| * * |\n";
break;
case 6:
std::cout << "| * * * |\n";
std::cout << "| |\n";
std::cout << "| * * * |\n";
break;
default:
std::cout << " *** Error: illegal die value ***\n";
break;
}
std::cout << "+-------+\n";
}

196
Fundamentals of C++ Programming
8.7. EXERCISES 197

The output of one run of Listing 8.12 (die.cpp) is


+-------+
| * * |
| |
| * * |
+-------+
+-------+
| * * * |
| |
| * * * |
+-------+
+-------+
| |
| * |
| |
+-------+

Since the values are pseudorandomly generated, actual output will vary from one run to the next.

8.7 Exercises

1. Suppose you need to compute the square root of a number in a C++ program. Would it be a good
idea to write the code to perform the square root calculation? Why or why not?

2. In C++ source code what is one way to help you distinguish a variable name from a function name?

3. Which one of the following values could be computed by the rand function?
4.5 34 -1 RAND_MAX + 1

4. What does clock_t represent?

5. What does CLOCKS_PER_SEC represent?

6. Ordinarily how often should a program call the srand function?

7. In Listing 8.2 (standardsquareroot.cpp), what does the main function do while the sqrt function is
computing the square root of the argument that main provides?

8. Consider each of the following code fragments below that could be part of a C++ program. Each
fragment contains a call to a standard C/C++ library function. Answer each question in one of the
following three ways:

• If the code fragment contains a compile-time error, write the word error for the answer.
• If the code fragment contains no compile-time errors and you can determine its output at
compile-time, provide the fragment’s literal output.
• If the code fragment contains no compile-time errors but you cannot determine its exact output
at compile-time, provide one possible evaluation and write the word example for the answer and
provide one possible literal output that the code fragment could produce.
197
Fundamentals of C++ Programming
8.7. EXERCISES 198

(a) std::cout << sqrt(4.5) << '\n';

(b) std::cout << sqrt(4.5, 3.1) << '\n';

(c) std::cout << rand(4) << '\n';

(d) double d = 16.0;


std::cout << sqrt(d) << '\n';

(e) std::cout << srand() << '\n';

(f) std::cout << rand() << '\n';

(g) int i = 16;


std::cout << sqrt(i) << '\n';

(h) std::cout << srand(55) << '\n';

(i) std::cout << tolower('A') << '\n';

(j) std::cout << exp() << '\n';

(k) std::cout << sqrt() << '\n';

(l) std::cout << toupper('E') << '\n';

(m) std::cout << toupper('e') << '\n';

(n) std::cout << toupper("e") << '\n';

(o) std::cout << exp(4.5) << '\n';

(p) std::cout << toupper('h', 5) << '\n';

(q) std::cout << ispunct('!') << '\n';

(r) std::cout << tolower("F") << '\n';

(s) char ch = 'D';


std::cout << tolower(ch) << '\n';

(t) std::cout << exp(4.5, 3) << '\n';

(u) std::cout << toupper('7') << '\n';

(v) double a = 5, b = 3;
std::cout << exp(a, b) << '\n';

198
Fundamentals of C++ Programming
8.7. EXERCISES 199

Figure 8.6 Right triangle

Hy
po
ten

Side 2
us
e

Side 1

(w) std::cout << exp(3, 5, 2) << '\n';

(x) std::cout << tolower(70) << '\n';

(y) double a = 5;
std::cout << exp(a, 3) << '\n';

(z) double a = 5;
std::cout << exp(3, a) << '\n';

9. From geometry: Write a computer program that given the lengths of the two sides of a right triangle
adjacent to the right angle computes the length of the hypotenuse of the triangle. (See Figure 8.6.)
If you are unsure how to solve the problem mathematically, do a web search for the Pythagorean
theorem.

199
Fundamentals of C++ Programming
8.7. EXERCISES 200

200
Fundamentals of C++ Programming
201

Chapter 9

Writing Functions

As programs become more complex, programmers must structure their programs in such a way as to ef-
fectively manage their complexity. Most humans have a difficult time keeping track of too many pieces of
information at one time. It is easy to become bogged down in the details of a complex problem. The trick
to managing complexity is to break down the problem into more manageable pieces. Each piece has its
own details that must be addressed, but these details are hidden as much as possible within that piece. The
problem is ultimately solved by putting these pieces together to form the complete solution.
So far all of our programs have been written within one function—main. As the number of statements
within a function increases, the function can become unwieldy. The code within such a function that does
all the work by itself is called monolithic code. Monolithic code that is long and complex is undesirable for
several reasons:

• It is difficult to write correctly. All the details in the entire piece of code must be considered when
writing any statement within that code.

• It is difficult to debug. If the sequence of code does not work correctly, it is often difficult to find
the source of the error. The effects of an erroneous statement that appears earlier in the code may not
become apparent until a correct statement later uses the erroneous statement’s incorrect result.

• It is difficult to extend. All the details in the entire sequence of code must be well understood before
it can be modified. If the code is complex, this may be a formidable task.

Using a divide and conquer strategy, a programmer can decompose a complicated function (like main)
into several simpler functions. The original function can then do its job by delegating the work to these
other functions. In this way the original function can be thought of as a “work coordinator.”
Besides their code organization aspects, functions allow us to bundle functionality into reusable parts. In
Chapter 8 we saw how library functions can dramatically increase the capabilities of our programs. While
we should capitalize on library functions as much as possible, sometimes we need a function exhibiting
custom behavior that is not provided by any standard function. Fortunately we can create our own functions,
and the same function may be used (called) in numerous places within a program. If the function’s purpose
is general enough and we write the function properly, we may be able to reuse the function in other programs
as well.
201
Fundamentals of C++ Programming
9.1. FUNCTION BASICS 202

9.1 Function Basics

Recall the “handwritten” square root code we saw in Listing 8.1 (computesquareroot.cpp). We know that the
better option is the standard library function sqrt; however, we will illustrate custom function development
by writing our own square root function based on the code in Listing 8.1 (computesquareroot.cpp). In
Listing 9.1 (customsquareroot.cpp) we see the definition for the square_root function.

Listing 9.1: customsquareroot.cpp


#include <iostream>
#include <iomanip>
#include <cmath>

// Compute an approximation of
// the square root of x
double square_root(double x) {
double diff;
// Compute a provisional square root
double root = 1.0;

do { // Loop until the provisional root


// is close enough to the actual root
root = (root + x/root) / 2.0;
//std::cout << "root is " << root << '\n';
// How bad is the approximation?
diff = root * root - x;
} while (diff > 0.0001 || diff < -0.0001);
return root;
}

int main() {
// Compare the two ways of computing the square root
for (double d = 1.0; d <= 10.0; d += 0.5)
std::cout << std::setw(7) << square_root(d) << " : " << sqrt(d) << '\n';
}

The main function in Listing 9.1 (customsquareroot.cpp) compares the behavior of our custom square_root
function to the sqrt library function. Its output:
1 : 1
1.22474 : 1.22474
1.41422 : 1.41421
1.58116 : 1.58114
1.73205 : 1.73205
1.87083 : 1.87083
2 : 2
2.12132 : 2.12132
2.23607 : 2.23607
2.34521 : 2.34521
2.44949 : 2.44949
2.54952 : 2.54951
2.64577 : 2.64575
2.73861 : 2.73861
2.82843 : 2.82843
2.91548 : 2.91548
202
Fundamentals of C++ Programming
9.1. FUNCTION BASICS 203

3 : 3
3.08221 : 3.08221
3.16228 : 3.16228

shows a few small differences in the results. Clearly we should use the standard sqrt function instead of
ours.
There are two aspects to every C++ function:

• Function definition. The definition of a function specifies the function’s return type and parameter
types, and it provides the code that determines the function’s behavior. In Listing 9.1 (customsquareroot.cpp)
the definition of the square_root function appears above the main function.

• Function invocation. A programmer uses a function via a function invocation. The main function
invokes both our square_root function and the sqrt function. Every function has exactly one
definition but may have many invocations.

A function definition consists of four parts:

• Name—every function in C++ has a name. The name is an identifier (see Section 3.3). As with
variable names, the name chosen for a function should accurately portray its intended purpose or
describe its functionality.

• Type—every function has a return type. If the function returns a value to its caller, its type corre-
sponds to the type of the value it returns. The special type void signifies that the function does not
return a value.

• Parameters—every function must specify the types of parameters that it accepts from callers. The
parameters appear in a parenthesized comma-separated list like in a function prototype (see Sec-
tion 8.1). Unlike function prototypes, however, parameters usually have names associated with each
type.

• Body—every function definition has a body enclosed by curly braces. The body contains the code to
be executed when the function is invoked.

Figure 9.1 dissects a our square_root function definition.


The simplest function accepts no parameters and returns no value to the caller. Listing 9.2 (simplefunction.cpp)
is a variation of Listing 4.1 (adder.cpp) that contains such a simple function definition.

Listing 9.2: simplefunction.cpp


#include <iostream>

// Definition of the prompt function


void prompt() {
std::cout << "Please enter an integer value: ";
}

int main() {
int value1, value2, sum;
std::cout << "This program adds together two integers.\n";
prompt(); // Call the function
std::cin >> value1;
203
Fundamentals of C++ Programming
9.1. FUNCTION BASICS 204

Figure 9.1 Function definition dissection

Type of value the Name of Type of value the


function computes function function requires the
caller to provide

double square_root(double x) {
double diff;
// Compute a provisional square root
double root = 1.0; The name the function
uses for the value
Body of do { // Loop until the provisional root
// is close enough to provided by the
the actual rootcaller
function
root = (root + x/root) / 2.0;
// How bad is the approximation?
diff = root * root - x;
} while (diff > 0.0001 || diff < -0.0001);
return root;
}

204
Fundamentals of C++ Programming
9.1. FUNCTION BASICS 205

prompt(); // Call the function again


std::cin >> value2;
sum = value1 + value2;
std::cout << value1 << " + " << value2 << " = " << sum << '\n';
}

The prompt function simply prints a message. The program runs as follows:

1. The program’s execution, like in all C++ programs, begins with the first executable statement in the
function named main. The first line in the main function simply declares some variables needed for
compiler housekeeping, so the next line actually begins the executable code.
2. The first executable statement prints the message of the program’s intent.
3. The next statement is a call of the prompt function. At this point the program’s execution transfers
to the body of the prompt function. The code within prompt is executed until the end of its body
or until a return statement is encountered. Since prompt contains no return statement, all of
prompt’s body (the one print statement) will be executed.
4. When prompt is finished, control is passed back to the point in main immediately after the call of
prompt.
5. The next action after prompt call reads the value of value1 from the keyboard.
6. A second call to prompt transfers control back to the code within the prompt function. It again
prints its message.
7. When the second call to prompt is finished, control passes back to main at the point of the second
input statement that assigns value2 from the keyboard.
8. The remaining two statements in main are executed, and then the program’s execution terminates.

As another simple example, consider Listing 9.3 (countto10.cpp).

Listing 9.3: countto10.cpp


#include <iostream>

int main() {
for (int i = 1; i <= 10; i++)
std::cout << i << '\n';
}

which simply counts to ten:


1
2
3
4
5
6
7
8
9
10
205
Fundamentals of C++ Programming
9.1. FUNCTION BASICS 206

If counting to ten in this way is something we want to do frequently within a program, we can write a
function as shown in Listing 9.4 (countto10func.cpp) and call it as many times as necessary.

Listing 9.4: countto10func.cpp


#include <iostream>

// Count to ten and print each number on its own line


void count_to_10() {
for (int i = 1; i <= 10; i++)
std::cout << i << '\n';
}

int main() {
std::cout << "Going to count to ten . . .";
count_to_10();
std::cout << "Going to count to ten again. . .";
count_to_10();
}

Our prompt and countto10 functions are a bit underwhelming. The prompt function could be
eliminated, and each call to prompt could be replaced with the statement in its body. The same could
be said for the countto10 function, although it is convenient to have the simple one-line statement that
hides the complexity of the loop. Using the prompt function does have one advantage, though. If prompt
is removed and the two calls to prompt are replaced with the print statement within prompt, we have
to make sure that the two messages printed are identical. If we simply call prompt, we know the two
messages printed will be identical because only one possible message can be printed (the one in the body
of prompt).
We can alter the behavior of a function through a mechanism called parameter passing. If a function
is written to accept information from the caller, the caller must supply the information in order to use the
function. The caller communicates the information via one or more parameters as required by the function.
The countto10 function does us little good if we sometimes want to count up to a different number.
Listing 9.5 (countton.cpp) generalizes Listing 9.4 (countto10func.cpp) to count as high as the caller needs.

Listing 9.5: countton.cpp


#include <iostream>

// Count to n and print each number on its own line


void count_to_n(int n) {
for (int i = 1; i <= n; i++)
std::cout << i << '\n';
}

int main() {
std::cout << "Going to count to ten . . .";
count_to_n(10);
std::cout << "Going to count to five . . .";
count_to_n(5);
}

When the caller, in this case main, issues the call


count_to_n(10);
206
Fundamentals of C++ Programming
9.1. FUNCTION BASICS 207

the argument 10 is assigned to n before the function’s statements begin executing.


A caller must pass exactly one integer parameter (or other type that is assignment-compatible with
integers) to count_to_n during a call. An attempt to do otherwise will result in a compiler error or
warning:
count_to_n(); // Error, missing parameter during the call
count_to_n(3, 5); // Error, too many parameters during the call
count_to_n(3.2); // Warning, possible loss of data (double to int)

We can enhance the prompt function’s capabilities as shown in Listing 9.6 (betterprompt.cpp)

Listing 9.6: betterprompt.cpp


#include <iostream>

// Definition of the prompt function


int prompt() {
int result;
std::cout << "Please enter an integer value: ";
std::cin >> result;
return result;
}

int main() {
int value1, value2, sum;
std::cout << "This program adds together two integers.\n";
value1 = prompt(); // Call the function
value2 = prompt(); // Call the function again
sum = value1 + value2;
std::cout << value1 << " + " << value2 << " = " << sum << '\n';
}

In this version, prompt takes care of the input, so main does not have to use any input statements. The
assignment statement within main:
value1 = prompt();

implies prompt is no longer a void function; it must return a value that can be assigned to the variable
value1. Furthermore, the value that prompt returns must be assignment compatible with an int be-
cause value1’s declared type is int. A quick look at the first line of prompt’s definition confirms our
assumption:
int prompt()

This indicates that prompt returns an int value.


Because prompt is declared to return an int value, it must contain a return statement. A return
statement specifies the exact value to return to the caller. When a return is encountered during a function’s
execution, control immediately passes back to the caller. The value of the function call is the value specified
by the return statement, so the statement
value1 = prompt();

assigns to the variable value1 the value indicated when the return statement executs.
207
Fundamentals of C++ Programming
9.1. FUNCTION BASICS 208

Note that in Listing 9.6 (betterprompt.cpp), we declared a variable named result inside the prompt
function. This variable is local to the function, meaning we cannot use this particular variable outside of
prompt. It also means we are free to use that same name outside of the prompt function in a different
context, and that use will not interfere with the result variable within prompt. We say that result is
a local variable.
We can further enhance our prompt function. Currently prompt always prints the same message.
Using parameters, we can customize the message that prompt prints. Listing 9.7 (evenbetterprompt.cpp)
shows how parameters are used to provide a customized message within prompt.

Listing 9.7: evenbetterprompt.cpp


#include <iostream>

// Definition of the prompt function


int prompt(int n) {
int result;
std::cout << "Please enter integer #" << n << ": ";
std::cin >> result;
return result;
}

int main() {
int value1, value2, sum;
std::cout << "This program adds together two integers.\n";
value1 = prompt(1); // Call the function
value2 = prompt(2); // Call the function again
sum = value1 + value2;
std::cout << value1 << " + " << value2 << " = " << sum << '\n';
}

In Listing 9.7 (evenbetterprompt.cpp), the parameter influences the message that it printed. The user is
now prompted to enter value #1 or value #2. The call
value1 = prompt(1);

passes the value 1 to the prompt function. Since prompt’s parameter is named n, the process works as if
the assignment statement
n = 1;

were executed as the first action within prompt.


In the first line of the function definition:
int prompt(int n)

n is called the formal parameter. A formal parameter is used like a variable within the function’s body, but
it is declared in the function’s parameter list; it is not declared in the function’s body. A formal parameter
is a parameter as used in the formal definition of the function.
At the point of the function call:
value1 = prompt(1);

the parameter (or argument) passed into the function, 1, is called the actual parameter. An actual parameter
is the parameter actually used during a call of the function. When a function is called, any actual parameters
208
Fundamentals of C++ Programming
9.1. FUNCTION BASICS 209

are assigned to their corresponding formal parameters, and the function begin executing. Another way to say
it is that during a function call, the actual parameters are bound to their corresponding formal parameters.

The parameters used within a function definition are called formal parameters.
Formal parameters behave as local variables within the function’s body; as such,
the name of a formal parameter will not conflict with any local variable or for-
mal parameter names from other functions. This means as a function developer
you may choose a parameter name that best represents the parameter’s role in the
function.

If you are writing a function, you cannot predict the caller’s actual parameters.
You must be able to handle any value the caller sends. The compiler will ensure
that the types of the caller’s parameters are compatible with the declared types of
your formal parameters.

To remember the difference between formal and actual parameters, remember this:
• A formal parameter is a parameter declared and used in a function’s formal
definition.
• An actual parameter is a parameter supplied by the caller when the caller
actually uses (invokes or calls) the function.

When the call


value1 = prompt(1);

is executed in main, and the statement


std::cout << "Please enter integer #" << n << ": ";

within the body of prompt is executed, n will have the value 1. Similarly, when the call
value2 = prompt(2);

is executed in main, and the statement


std::cout << "Please enter integer #" << n << ": ";

within the body of prompt is executed, n will have the value 2. In the case of
value1 = prompt(1);

n within prompt is bound to 1, and in the case of


value2 = prompt(2);

n within prompt is bound to 2.

209
Fundamentals of C++ Programming
9.2. USING FUNCTIONS 210

A function’s definition requires that all formal parameters be declared in the paren-
theses following the function’s name. A caller does not provide actual parameter
type declarations when calling the function. Given the square_root function
defined in Listing 9.1 (customsquareroot.cpp), the following caller code fragment
is illegal:
double number = 25.0;
// Legal, pass the variable's value to the function
std::cout << square_root(number) << '\n';
// Illegal, do not declare the parameter during the call
std::cout << square_root(double number) << '\n';
The function definition is responsible for declaring the types of its parameters, not
the caller.

9.2 Using Functions

The general form of a function definition is

type name( parameterlist ) {


body
}
• The type of the function indicates the type of value the function returns. Often a function will perform
a calculation and the result of the calculation must be communicated back to the place where the
function was invoked. The special type void indicates that the function does not return a value.
• The name of the function is an identifier (see Section 3.3). The function’s name should indicate the
purpose of the function.
• The parameterlist is a comma separated list of pairs of the form
type name
where type is a C++ type and name is an identifier representing a parameter. The caller of the func-
tion communicates information into the function via parameters. The parameters specified in the
parameter list of a function definition are called formal parameters. A parameter is also known as an
argument. The parameter list may be empty; an empty parameter list indicates that no information
may be passed into the function by the caller.
• The body is the sequence of statements, enclosed within curly braces, that define the actions that the
function is to perform. The statements may include variable declarations, and any variables declared
within the body are local to that function.
The body may contain only one statement, many statements, or no statements at all; regardless, the
curly braces always are required.

Observe that multiple pieces of information can be passed into a function via multiple parameters,
but only one piece of information can be passed out of the function via the return value. Recall the greatest
210
Fundamentals of C++ Programming
9.2. USING FUNCTIONS 211

Figure 9.2 Cutting plywood

18 inches

24 inches

Figure 9.3 Squares too small

3
3

18 inches

24 inches

common divisor (also called greatest common factor) from elementary mathematics. To determine the GCD
of 24 and 18 we list all of their common factors and select the largest one:
24: 1, 2, 3, 4, 6 , 8, 12, 24
The greatest common divisor function is useful when reducing fractions
18: 1, 2, 3, 6 , 9, 18
18
to lowest terms; for example, consider the fraction . The greatest common divisor of 18 and 24 is 6, and
24
18 ÷ 6 3
so we divide the numerator and the denominator of the fraction by 6: = . The GCD function has
24 ÷ 6 4
applications in other areas besides reducing fractions to lowest terms. Consider the problem of dividing a
piece of plywood 24 inches long by 18 inches wide into square pieces of maximum size without wasting
any material. Since the GCD(24, 18) = 6, we can cut the plywood into twelve 6 inch × 6 inch square pieces
as shown in Figure 9.2.
If we cut the plywood into squares of any other size without wasting the any of the material, the squares
would have to be smaller than 6 inches × 6 inches; for example, we could make forty-eight 3 inch × 3 inch
squares as shown in pieces as shown in Figure 9.3.
If we cut squares larger than 6 inches × 6 inches, not all the plywood can be used to make the squares.
211
Fundamentals of C++ Programming
9.2. USING FUNCTIONS 212

Figure 9.4 Squares too large

9 in.

9 in.

Waste
18 inches

24 inches

8 in.

8 in.

18 inches

Waste

24 inches

Figure 9.4. shows how some larger squares would fare.


In addition to basic arithmetic and geometry, the GCD function plays a vital role in cryptography,
enabling secure communication across an insecure network.

Listing 9.8: gcdprog.cpp


#include <iostream>

int main() {
// Prompt user for input
int num1, num2;
std::cout << "Please enter two integers: ";
std::cin >> num1 >> num2;

// Determine the smaller of num1 and num2


int min = (num1 < num2) ? num1 : num2;

// 1 is definitely a common factor to all ints


int largestFactor = 1;
for (int i = 2; i <= min; i++)
212
Fundamentals of C++ Programming
9.2. USING FUNCTIONS 213

if (num1 % i == 0 && num2 % i == 0)


largestFactor = i; // Found larger factor
std::cout << largestFactor << '\n';
}

Listing 9.8 (gcdprog.cpp) implements a straight-forward but naive algorithm that seeks potential factors by
considering every integer less than the smaller of the two values provided by the user. This algorithm is not
very efficient, especially for larger numbers. Its logic is easy to follow, with no deep mathematical insight
required. Soon we will see a better algorithm for computing GCD.
If we need to compute the GCD from several different places within our program, we should package
the code in a function rather than copying it to multiple places. The following code fragment defines a C++
function that that computes the greatest common divisor of two integers. It determines the largest factor
(divisor) common to its parameters:
int gcd(int num1, int num2) {
// Determine the smaller of num1 and num2
int min = (num1 < num2) ? num1 : num2;
// 1 is definitely a common factor to all ints
int largestFactor = 1;
for (int i = 2; i <= min; i++)
if (num1 % i == 0 && num2 % i == 0)
largestFactor = i; // Found larger factor
return largestFactor;
}

This function is named gcd and expects two integer arguments. Its formal parameters are named num1 and
num2. It returns an integer result. Its body declares three local variables: min, largestFactor, and i
(i is local to the for statement). The last line in its body is a return statement. A return statement is
required for functions that return a value. A void function is not required to have a return statement. If a
void function does have a return statement, it must simply consist of return followed by a semicolon
(in other words, it cannot return a value, like gcd’s return statement does). A void function that does
not contain a return statement simply returns at the end of its body.
Recall from Section 6.5 that local variables have meaning only within their scope. This means that when
you write a function you can name a local variable without fear that its name may be used already in another
part of the program. Two different functions can use local variables named x, and these are two different
variables that have no influence on each other. Anything local to a function definition is hidden to all code
outside that function definition.
Since a formal parameter is a local variable, you can reuse the names of formal parameters in different
functions without a problem.
It may seem strange that we can use the same name in two different functions within the same program
to refer to two distinct variables. The block of statements that makes up a function definition constitutes
a context for local variables. A simple analogy may help. In the United States, many cities have a street
named Main Street; for example, there is a thoroughfare named Main Street in San Francisco, California.
Dallas, Texas also has a street named Main Street. Each city and town provides its own context for the use
of the term Main Street. A person in San Francisco asking “How do I get to Main Street?” will receive the
directions to San Francisco’s Main Street, while someone in Dallas asking the same question will receive
Dallas-specific instructions. In a similar manner, assigning a variable within a function block localizes its
identity to that function. We can think of a program’s execution as a person traveling around the U.S. When
in San Francisco, all references to Main Street mean San Francisco’s Main Street, but when the traveler
213
Fundamentals of C++ Programming
9.2. USING FUNCTIONS 214

arrives in Dallas, the term Main Street means Dallas’ Main Street. A program’s thread of execution cannot
execute more than one statement at a time, which means the compiler can use its current context to interpret
any names it encounters within a statement. Similarly, at the risk of overextending the analogy, a person
cannot be physically located in more than one city at a time. Furthermore, Main Street may be a bustling,
multi-lane boulevard in one large city, but a street by the same name in a remote, rural township may be a
narrow dirt road! Similarly, two like-named variables may have two completely different types. A variable
named x in one function may represent an integer, while a different function may use a string variable
named x.
Another advantage of local variables is that they occupy space in the computer’s memory only when
the function is executing. Space is allocated for local variables and parameters when the function begins
executing. When the function is finished and control returns to the caller, the variables and parameters go
out of scope, and the memory they held is freed up for other purposes within the running program. This
process of local variable allocation and deallocation happens each time a caller invokes the function. More
information about how C++ handles memory management during a program’s execution can be found in
Section 18.1.
Once we have written a complete function definition we can use the function within our program. We
invoke a programmer-defined function in exactly the same way as a standard library function like sqrt
(Section 8.2) or rand (Section 8.6). If the function returns a value (that is, it is not declared void), then we
can use its invocation anywhere an expression of that type is allowed. The parameters used for the function
call are known as actual parameters. The function gcd can be called as part of an assignment statement:
int factor = gcd(val, 24);

This call uses the variable val as its first actual parameter and the literal value 24 as its second actual
parameter. Variables, expressions, and literals can be freely used as actual parameters. The function then
computes and returns its result. This result is assigned to the variable factor.
How does the function call and parameter mechanism work? It’s actually quite simple. The actual
parameters, in order, are assigned (bound) to each of the formal parameters in the function definition, then
control is passed to the body of the function. When the function’s body is finished executing, control passes
back to the point in the program where the function was called. The value returned by the function, if any,
replaces the function call expression. In the statement
int factor = gcd(val, 24);

an integer value is assigned to factor. The expression on the right is a function call, so the function is
invoked to determine what to assign. The value of the variable val is assigned to the formal parameter
num1, and the literal value 24 is assigned to the formal parameter num2. The body of the gcd function is
then executed. When the return statement in the body is encountered, program execution returns back to
where the function was called. The argument of the return statement becomes the value that is assigned to
factor. This process of copying actual parameters to formal parameters works exactly like during assign-
ment. This means the compiler, where possible, automatically will widen or narrow (see Section 4.2) the
value of an actual parameter to make it compatible with its corresponding formal parameter; for example, if
val is declared to a char, its value would automatically be copied to a temporary location and converted
to an int. This temporary value would then be bound to the formal parameter num1. Note that gcd could
be called from many different places within the same program, and, since different parameter values could
be passed at each of these different invocations, gcd could compute a different result at each invocation.
Other invocation examples include:

• std::cout << gcd(36, 24);

214
Fundamentals of C++ Programming
9.3. PASS BY VALUE 215

This example simply prints the result of the invocation. The value 36 is bound to num1
and 24 is bound to num2 for the purpose of the function call. The value 12 will be printed,
since 12 is the greatest common divisor of 36 and 24..

• x = gcd(x - 2, 24);

The execution of this statement would evaluate x - 2 and bind its value to num1. num2
would be assigned 24. The result of the call is then assigned to x. Since the right side of
the assignment statement is evaluated before being assigned to the left side, the original
value of x is used when calculating x - 2, and the function return value then updates x.

• x = gcd(x - 2, gcd(10, 8));

This example shows two invocations in one statement. Since the function returns an integer
value its result can itself be used as an actual parameter in a function call. Passing the
result of one function call as an actual parameter to another function call is called function
composition.

The compiler will report an error if a function call does not agree with the function’s definition. Possible
problems include:

• Number of actual parameters do not agree with the number of formal parameters. The number
of parameters must agree exactly. For example, the statement
int factor = gcd(24); // Error: too few parameters

is illegal given the above definition of gcd, since only one actual parameter is provided when two
are required.

• Passing an actual parameter that is not assignment compatible with the formal parameter. For
example, passing the std::cout object when an int has been defined, as in
int factor = gcd(36, std::cout); // Error: second parameter is wrong type

The compiler will detect that std::cout is not a valid int and report an error.

• Using the result in a context where an expression of that type is not allowed. For example, a
function that returns void cannot be used where an int is expected:
std::cout << srand(2); // Error: srand does not return anything

The compiler will disallow this code.

9.3 Pass by Value

The default parameter passing mechanism in C++ is classified as pass by value, also known as call by value.
This means the value of the actual parameter is copied to the formal parameter for the purpose of executing
the function’s code. Since it is working on a copy of the actual parameter, the function’s execution cannot
affect the value of the actual parameter owned by the caller.
Listing 9.9 (passbyvalue.cpp) illustrates the consequences of pass by value.
215
Fundamentals of C++ Programming
9.3. PASS BY VALUE 216

Listing 9.9: passbyvalue.cpp


#include <iostream>

/*
* increment(x)
* Illustrates pass by value protocol.
*/
void increment(int x) {
std::cout << "Beginning execution of increment, x = "
<< x << '\n';
x++; // Increment x
std::cout << "Ending execution of increment, x = "
<< x << '\n';
}

int main() {
int x = 5;
std::cout << "Before increment, x = " << x << '\n';
increment(x);
std::cout << "After increment, x = " << x << '\n';
}

For additional drama we chose to name the actual parameter the same as the formal parameter. Since the
actual parameter and formal parameter are declared and used in different contexts and represent completely
different memory locations, their names can be the same without any problems.
Listing 9.9 (passbyvalue.cpp) produces
Before increment, x = 5
Beginning execution of increment, x = 5
Ending execution of increment, x = 6
After increment, x = 5

The memory for the variable x in main is unaffected since increment works on a copy of the actual
parameter.
C++ supports another way of passing parameters called pass by reference. Pass by reference is intro-
duced in Section 10.9.
A function communicates its return value to the caller in the same way that the caller might pass a
parameter by value. In the prompt function we saw earlier:
int prompt(int n) {
int result;
std::cout << "Please enter integer #" << n << ": ";
std::cin >> result;
return result;
}

the return statement is


return result;

The variable result is local to prompt. We informally may say we are returning the result variable,
but, in fact, we really are returning only the value of the result variable. The caller has no access to
216
Fundamentals of C++ Programming
9.4. FUNCTION EXAMPLES 217

the local variables declared within any function it calls. In fact, the local variables for a function exist
only when the function is active (that is, executing). When the function returns to its caller all of its local
variables disappear from memory. During subsequent invocations, the function’s local variables reappear
when the function becomes active and disappear again when it finishes.

9.4 Function Examples

This section contains a number of examples of how we can use functions to organize a program’s code.

9.4.1 Better Organized Prime Generator

Listing 9.10 (primefunc.cpp) is a simple enhancement of Listing 8.5 (moreefficientprimes.cpp). It uses the
square root optimization and adds a separate is_prime function.

Listing 9.10: primefunc.cpp


#include <iostream>
#include <cmath>

/*
* is_prime(n)
* Determines the primality of a given value
* n an integer to test for primality
* Returns true if n is prime; otherwise, returns false
*/
bool is_prime(int n) {
bool result = true; // Provisionally, n is prime
double r = n, root = sqrt(r);
// Try all possible factors from 2 to the square
// root of n
for (int trial_factor = 2;
result && trial_factor <= root; trial_factor++)
result = (n % trial_factor != 0);
return result;
}

/*
* main
* Tests for primality each integer from 2
* up to a value provided by the user.
* If an integer is prime, it prints it;
* otherwise, the number is not printed.
*/
int main() {
int max_value;
std::cout << "Display primes up to what value? ";
std::cin >> max_value;
for (int value = 2; value <= max_value; value++)
if (is_prime(value)) // See if value is prime
std::cout << value << " "; // Display the prime number
std::cout << '\n'; // Move cursor down to next line
}
217
Fundamentals of C++ Programming
9.4. FUNCTION EXAMPLES 218

Listing 9.10 (primefunc.cpp) illustrates several important points about well-organized programs:

• The complete work of the program is no longer limited to the main function. The effort to test for
primality is delegated to a separate function. main is focused on a simpler task: generating all the
numbers to be considered and using another function (is_prime) to do the hard work of deter-
mining if a given number is prime. main is now simpler and more logically coherent. A function
is coherent when it is focused on a single task. Coherence is a desirable property of functions. If a
function becomes too complex by trying to do too many different things, it can be more difficult to
write correctly and debug when problems are detected. A complex function should be decomposed
into several, smaller, more coherent functions. The original function would then call these new sim-
pler functions to accomplish its task. Here, main is not concerned about how to determine if a given
number is prime; main simply delegates the work to is_prime and makes use of the is_prime
function’s findings.

• Each function is preceded by a thorough comment that describes the nature of the function. It explains
the meaning of each parameter, and it indicates what the function should return. The comment for
main may not be as thorough as for other functions; this is because main usually has no parameters,
and it always returns a code to the operating system upon the program’s termination.

• While the exterior comment indicates what the function is to do, comments within each function
explain in more detail how the function accomplishes its task.

The call to is_prime returns true or false depending on the value passed to it. This means we can
express a condition like
if (is_prime(value) == true) . . .

more compactly as
if (is_prime(value)) . . .

because if is_prime(value) is true, true == true is true, and if is_prime(value) is false,


false == true is false. The expression is_prime(value) suffices.
Just as it is better for a loop to have exactly one entry point and exactly one exit point, preferably a
function will have a single return statement. Simple functions with a small number of returns are
generally tolerable, however. Consider the following version of is_prime:
bool is_prime(int n) {
for (int trialFactor = 2;
trialFactor <= sqrt(static_cast<double>(n));
trialFactor++)
if (n % trialFactor == 0) // Is trialFactor a factor?
return false; // Yes, return right away
return true; // Tried them all, must be prime
}

This version uses two return statements, but eliminates the need for a local variable (result). Because
a return statement exits the function immediately, no break statement is necessary. The two return
statements are close enough textually in source code that the logic is fairly transparent.
218
Fundamentals of C++ Programming
9.4. FUNCTION EXAMPLES 219

9.4.2 Command Interpreter

Some functions are useful even if they accept no information from the caller and return no result. List-
ing 9.11 (calculator.cpp) uses such a function.

Listing 9.11: calculator.cpp


#include <iostream>
#include <cmath>

/*
* help_screen
* Displays information about how the program works
* Accepts no parameters
* Returns nothing
*/
void help_screen() {
std::cout << "Add: Adds two numbers\n";
std::cout << " Example: a 2.5 8.0\n";
std::cout << "Subtract: Subtracts two numbers\n";
std::cout << " Example: s 10.5 8.0\n";
std::cout << "Print: Displays the result of the latest operation\n";
std::cout << " Example: p\n";
std::cout << "Help: Displays this help screen\n";
std::cout << " Example: h\n";
std::cout << "Quit: Exits the program\n";
std::cout << " Example: q\n";
}

/*
* menu
* Display a menu
* Accepts no parameters
* Returns the character entered by the user.
*/
char menu() {
// Display a menu
std::cout << "=== A)dd S)ubtract P)rint H)elp Q)uit ===\n";
// Return the char entered by user
char ch;
std::cin >> ch;
return ch;
}

/*
* main
* Runs a command loop that allows users to
* perform simple arithmetic.
*/
int main() {
double result = 0.0, arg1, arg2;
bool done = false; // Initially not done
do {
switch (menu()) {
case 'A': // Addition
219
Fundamentals of C++ Programming
9.4. FUNCTION EXAMPLES 220

case 'a':
std::cin >> arg1 >> arg2;
result = arg1 + arg2;
std::cout << result << '\n';
break;
case 'S': // Subtraction
case 's':
std::cin >> arg1 >> arg2;
result = arg1 - arg2;
// Fall through, so it prints the result
case 'P': // Print result
case 'p':
std::cout << result << '\n';
break;
case 'H': // Display help screen
case 'h':
help_screen();
break;
case 'Q': // Quit the program
case 'q':
done = true;
break;
}
}
while (!done);
}

The help_screen function needs no information from main, nor does it return a result. It behaves
exactly the same way each time it is called. The menu function returns the character entered by the user.

9.4.3 Restricted Input

Listing 7.3 (betterinputonly.cpp) forces the user to enter a value within a specified range. We now can easily
adapt that concept to a function. Listing 9.12 (betterinputfunc.cpp) uses a function named get_int_range
that does not return until the user supplies a proper value.

Listing 9.12: betterinputfunc.cpp


#include <iostream>

/*
* get_int_range(first, last)
* Forces the user to enter an integer within a
* specified range
* first is either a minimum or maximum acceptable value
* last is the corresponding other end of the range,
* either a maximum or minimum * value
* Returns an acceptable value from the user
*/
int get_int_range(int first, int last) {
// If the larger number is provided first,
// switch the parameters
if (first > last) {
int temp = first;
220
Fundamentals of C++ Programming
9.4. FUNCTION EXAMPLES 221

first = last;
last = temp;
}
// Insist on values in the range first...last
std::cout << "Please enter a value in the range "
<< first << "..." << last << ": ";
int in_value; // User input value
bool bad_entry;
do {
std::cin >> in_value;
bad_entry = (in_value < first || in_value > last);
if (bad_entry) {