. . . with such violence I fell to the ground that I found myself stunned, and in a hole nine fathoms under the grass. . . . Looking down, I observed that I had on a pair of boots with exceptionally sturdy straps. Grasping them firmly, I pulled (repeatedly) with all my might.
—Rudolph Raspe, The Singular Adventures of Baron Munchausen
In this chapter, you’ll begin by setting up a C++ development environment, which is the collection of tools that enables you to develop C++ software. You’ll use the development environment to compile your first C++ console application, a program that you can run from the command line. Then you’ll learn the main components of the development environment along with the role they play in generating the application you’ll write. The chapters that follow will cover enough C++ essentials to construct useful example programs.
C++ has a reputation for being hard to learn. It’s true that C++ is a big, complex, and ambitious language and that even veteran C++ programmers regularly learn new patterns, features, and usages.
A major source of nuance is that C++ features mesh together so tightly. Unfortunately, this often causes some distress to newcomers. Because C++ concepts are so tightly coupled, it’s just not clear where to jump in. Part I of this book charts a deliberate, methodical course through the tumult, but it has to begin somewhere. This chapter covers just enough to get you started. Don’t sweat the details too much!
In this section, you’ll write a simple C++ program and then compile and run it. You write C++ source code into human-readable text files called source files. Then you use a compiler to convert your C++ into executable machine code, which is a program that computers can run.
Let’s dive in and create your first C++ source file.
Open your favorite text editor. If you don’t have a favorite just yet, try Vim, Emacs, or gedit on Linux; TextEdit on Mac; or Notepad on Windows. Enter the code in Listing 1-1 and save the resulting file to your desktop as main.cpp.
#include <cstdio> ➊ int main➋(){ printf("Hello, world!"); ➌ return 0; ➍ } -------------------------------------------------------------------------- Hello, world! ➌
Listing 1-1: Your first C++ program prints Hello, world! to the screen.
The Listing 1-1 source file compiles to a program that prints the characters Hello, world! to the screen. By convention, C++ source files have a .cpp extension.
NOTE
In this book, listings will include any program output immediately after the program’s source; the output will appear in gray. Numerical annotations will correspond with the line that produced the output. The printf statement in Listing 1-1, for example, is responsible for the output Hello, world!, so these share the same annotation ➌.
As shown in Listing 1-1, C++ programs have a single entry point called the main function ➋. An entry point is a function that executes when a user runs a program. Functions are blocks of code that can take inputs, execute some instructions, and return results.
Within main, you call the function printf, which prints the characters Hello, world! to the console ➌. Then the program exits by returning the exit code 0 to the operating system ➍. Exit codes are integer values that the operating system uses to determine how well a program ran. Generally, a zero (0) exit code means the program ran successfully. Other exit codes might indicate a problem. Having a return statement in main is optional; the exit code defaults to 0.
The printf function is not defined in the program; it’s in the cstdio library ➊.
Libraries are helpful code collections you can import into your programs to prevent having to reinvent the wheel. Virtually every programming language has some way of incorporating library functionality into a program:
Listing 1-1 included cstdio ➊, a library that performs input/output operations, such as printing to the console.
After writing the source code for a C++ program, the next step is to turn your source code into an executable program. The compiler tool chain (or tool chain) is a collection of three elements that run one after the other to convert source code into a program:
All C++ development environments contain a way to edit source code and a compiler tool chain to turn that source code into a program. Often, development environments also contain a debugger—an invaluable program that lets you step through a program line by line to find errors.
When all of these tools—the text editor, the compiler tool chain, and the debugger—are bundled into a single program, that program is called an interactive development environment (IDE). For beginners and veterans alike, IDEs can be a huge productivity booster.
NOTE
Unfortunately, C++ doesn’t have an interpreter with which to interactively execute C++ code snippets. This is different from other languages like Python, Ruby, and JavaScript, which do have interpreters. Some web applications exist that allow you to test and share small C++ code snippets. See Wandbox (https://wandbox.org/), which allows you to compile and run code, and Matt Godbolt’s Compiler Explorer (https://www.godbolt.org/), which allows you to inspect the assembly code that your code generates. Both work on a variety of compilers and systems.
Each operating system has its own source code editors and compiler tool chain, so this section is broken out by operating system. Skip to the one that is relevant to you.
At press time, the most popular C++ compiler for Microsoft Windows is the Microsoft Visual C++ Compiler (MSVC). The easiest way to obtain MSVC is to install the Visual Studio 2017 IDE as follows:
Set up a new project:
Figure 1-1: The Visual Studio 2017 New Project wizard
Figure 1-2: Adding an existing source file to a Visual Studio 2017 project
If you’re running macOS, you should install the Xcode development environment.
To compile and run your program, open the Xcode IDE and follow these steps:
Figure 1-3: The New Project dialog in Xcode
On Linux, you can choose between two main C++ compilers: GCC and Clang. At press time, the latest stable release is 9.1 and the latest major Clang release is 8.0.0. In this section, you’ll install both. Some users find the error messages from one to be more helpful than the other.
NOTE
GCC is an initialism for GNU Compiler Collection. GNU, pronounced “guh-NEW,” is a recursive acronym for “GNU’s Not Unix!” GNU is a Unix-like operating system and a collection of computer software.
Try to install GCC and Clang from your operating system’s package manager, but beware. Your default repositories might have old versions that may or may not have C++ 17 support. If your version doesn’t have C++ 17 support, you won’t be able to compile some examples in the book, so you’ll need to install updated versions of GCC or Clang. For brevity, this chapter covers how to do this on Debian and from source. You can either investigate how to perform corollary actions on your chosen Linux flavor or set up a development environment with one of the operating systems listed in this chapter.
Depending on what software the Personal Package Archives contain when you’re reading this chapter, you might be able to install GCC 8.1 and Clang 6.0.0 directly using Advanced Package Tool (APT), which is Debian’s package manager. This section shows how to install GCC and Clang on Ubuntu 18.04, the latest LTS Ubuntu version at press time.
$ sudo apt update && sudo apt upgrade
$ sudo apt install g++-8 clang-6.0
$ g++-8 –version g++-8 (Ubuntu 8-20180414-1ubuntu2) 8.0.1 20180414 (experimental) [trunk revision 259383] Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions.There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ clang++-6.0 --version clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin
If either command returns an error stating that the command wasn’t found, the corresponding compiler did not install correctly. Try searching for information on the error you receive, especially in the documentation and forums for your respective package manager.
If you can’t find the latest GCC or Clang versions with your package manager (or your Unix variant doesn’t have one), you can always install GCC from source. Note that this takes a lot of time (as much as several hours), and you might need to get your hands dirty: installation often runs into errors that you’ll need to research to resolve. To install GCC, follow the instructions available at https://gcc.gnu.org/. This section summarizes the far more extensive documentation available on that site.
NOTE
For brevity, this tutorial doesn’t detail Clang installation. Refer to https://clang.llvm.org/ for more information.
To install GCC 8.1 from source, do the following:
$ sudo apt update && sudo apt upgrade
$ gpg --keyserver keyserver.ubuntu.com --recv C3C45C06 gpg: requesting key C3C45C06 from hkp server keyserver.ubuntu.com gpg: key C3C45C06: public key "Jakub Jelinek <[email protected]>" imported gpg: key C3C45C06: public key "Jakub Jelinek <[email protected]>" imported gpg: no ultimately trusted keys found gpg: Total number processed: 2 gpg: imported: 2 (RSA: 1)
Verify what you downloaded:
$ gpg --verify gcc-8.1.0.tar.gz.sig gcc-8.1.0.tar.gz gpg: Signature made Wed 02 May 2018 06:41:51 AM DST using DSA key ID C3C45C06 gpg: Good signature from "Jakub Jelinek <[email protected]>" gpg: WARNING: This key is not certified with a trusted signature! gpg: There is no indication that the signature belongs to the owner. Primary key fingerprint: 33C2 35A3 4C46 AA3F FB29 3709 A328 C3A2 C3C4 5C06
The warnings you see mean that I haven’t marked the signer’s certificate as trusted on my machine. To verify that the signature belongs to the owner, you’ll need to verify the signing key using some other means (for example, by meeting the owner in person or by verifying the primary key fingerprint out of band). For more information about GNU Privacy Guard (GPG), refer to PGP & GPG: Email for the Practical Paranoid by Michael W. Lucas or browse to https://gnupg.org/download/integrity_check.html for specific information about GPG’s integrity-checking facilities.
$ tar xzf gcc-8.1.0.tar.gz
$ cd gcc-8.1.0
$ ./contrib/download_prerequisites --snip-- gmp-6.1.0.tar.bz2: OK mpfr-3.1.4.tar.bz2: OK mpc-1.0.3.tar.gz: OK isl-0.18.tar.bz2: OK All prerequisites downloaded successfully.
$ mkdir objdir $ cd objdir $ ../configure --disable-multilib checking build system type... x86_64-pc-linux-gnu checking host system type... x86_64-pc-linux-gnu --snip-- configure: creating ./config.status config.status: creating Makefile
Instructions are available at https://gcc.gnu.org/install/configure.html.
$ make
Full instructions are available at https://gcc.gnu.org/install/build.html.
$ make -k check
Full instructions are available at https://gcc.gnu.org/install/test.html.
$ make install
This command places a handful of binaries into your operating system’s default executable directory, which is usually /usr/local/bin. Full instructions are available at https://gcc.gnu.org/install/.
$ x86_64-pc-linux-gnu-gcc-8.1.0 --version
If you get an error indicating that the command was not found, your installation did not succeed. Refer to the gcc-help mailing list at https://gcc.gnu.org/ml/gcc-help/.
NOTE
You might want to alias the cumbersome x86_64-pc-linux-gnu-gcc-8.1.0 to something like g++8, for example, using a command like this:
$ sudo ln -s /usr/local/bin/x86_64-pc-linux-gnu-gcc-8.1.0 /usr/local/bin/g++8
$ x86_64-pc-linux-gnu-gcc-8.1.0 main.cpp -o hello
If you’d rather not work with one of the aforementioned IDEs, you can write C++ code using a simple text editor like Notepad (Windows), TextEdit (Mac), or Vim (Linux); however, a number of excellent editors are designed specifically for C++ development. Choose the environment that makes you most productive.
If you’re running Windows or macOS, you already have a high-quality, fully featured IDE at your disposal, namely Visual Studio or Xcode. Linux options include Qt Creator (https://www.qt.io/ide/), Eclipse CDT (https://eclipse.org/cdt/), and JetBrains’s CLion (https://www.jetbrains.com/clion/). If you’re a Vim or Emacs user, you’ll find plenty of C++ plug-ins.
NOTE
If cross-platform C++ is important to you, I highly recommend taking a look at Jetbrains’s CLion. Although CLion is a paid product, unlike many of its competitors, at press time Jetbrains does offer reduced-price and free licenses for students and open source project maintainers.
This section gives you just enough context to support the example code in the chapters to come. You’ll have questions about the details, and the coming chapters will answer them. Until then, don’t panic!
C++ is an object-oriented language. Objects are abstractions with state and behavior. Think of a real-world object, such as a light switch. You can describe its state as the condition that the switch is in. Is it on or off? What is the maximum voltage it can handle? What room in the house is it in? You could also describe the switch’s behavior. Does it toggle from one state (on) to another state (off)? Or is it a dimmer switch, which can be set to many different states between on and off?
The collection of behaviors and states describing an object is called its type. C++ is a strongly typed language, meaning each object has a predefined data type.
C++ has a built-in integer type called int. An int object can store whole numbers (its state), and it supports many math operations (its behavior).
To perform any meaningful tasks with int types, you’ll create some int objects and name them. Named objects are called variables.
You declare variables by providing their type, followed by their name, followed by a semicolon. The following example declares a variable called the_answer with type int:
int➊ the_answer➋;
The type, int ➊, is followed by the variable name, the_answer ➋.
When you declare variables, you initialize them. Object initialization establishes an object’s initial state, such as setting its value. We’ll delve into the details of initialization in Chapter 2. For now, you can use the equal sign (=) following a variable declaration to set the variable’s initial value. For example, you could declare and assign the_answer in one line:
int the_answer = 42;
After running this line of code, you have a variable called the_answer with type int and value 42. You can assign variables equal to the result of math expressions, such as:
int lucky_number = the_answer / 6;
This line evaluates the expression the_answer / 6 and assigns the result to lucky_number. The int type supports many other operations, such as addition +, subtraction -, multiplication *, and modulo division %.
NOTE
If you aren’t familiar with modulo division or are wondering what happens when you divide two integers and there’s a remainder, you’re asking great questions. And those great questions will be answered in detail in Chapter 7.
Conditional statements allow you to make decisions in your programs. These decisions rest on Boolean expressions, which evaluate to true or false. For example, you can use comparison operators, such as “greater than” or “not equal to,” to build Boolean expressions.
Some basic comparison operators that work with int types appear in the program in Listing 1-2.
int main() { int x = 0; 42 == x; // Equality 42 != x; // Inequality 100 > x; // Greater than 123 >= x; // Greater than or equal to -10 < x; // Less than -99 <= x; // Less than or equal to }
Listing 1-2: A program using comparison operators
This program produces no output (compile and run Listing 1-2 to verify this). While the program doesn’t produce any output, compiling it helps to verify that you’ve written valid C++. To generate more interesting programs, you’d use a conditional statement like if.
An if statement contains a Boolean expression and one or more nested statements. Depending on whether the Boolean evaluates to true or false, the program decides which nested statement to execute. There are several forms of if statements, but the basic usage follows:
if (➊boolean-expression) ➋statement
If the Boolean expression ➊ is true, the nested statement ➋ executes; otherwise, it doesn’t.
Sometimes, you’ll want a group of statements to run rather than a single statement. Such a group is called a compound statement. To declare a compound statement, simply wrap the group of statements in braces { }. You can use compound statements within if statements as follows:
if (➊boolean-expression) { ➋ statement1; statement2; --snip-- }
If the Boolean expression ➊ is true, all the statements in the compound statement ➋ execute; otherwise, none of them do.
You can elaborate the if statement using else if and else statements. These optional additions allow you to describe more complicated branching behavior, as shown in Listing 1-3.
➊ if (boolean-expression-1) statement-1 ➋ else if (boolean-expression-2) statement-2 ➌ else statement-3
Listing 1-3: An if statement with else if and else branches
First, boolean-expression-1 ➊ is evaluated. If boolean-expression-1 is true, statement-1 is evaluated, and the if statement stops executing. If boolean-expression-1 is false, boolean-expression-2 ➋ is evaluated. If true, statement-2 is evaluated. Otherwise, statement-3 ➌ is evaluated. Note that statement-1, statement-2, and statement-3 are mutually exclusive and together they cover all possible outcomes of the if statement. Only one of the three will be evaluated.
You can include any number of else if clauses or omit them entirely. As with the initial if statement, the Boolean expression for each else if clause is evaluated in order. When one of these Boolean expressions evaluates to true, evaluation stops and the corresponding statement executes. If no else if evaluates to true, the else clause’s statement-3 always executes. (As with the else if clauses, the else is optional.)
Consider Listing 1-4, which uses an if statement to determine which statement to print.
#include <cstdio> int main() { int x = 0; ➊ if (x > 0) printf("Positive."); else if (x < 0) printf("Negative."); else printf("Zero."); } ----------------------------------------------------------------------- Zero.
Listing 1-4: A program with conditional behavior
Compile the program and run it. Your result should also be Zero. Now change the x value ➊. What does the program print now?
NOTE
Notice that main in Listing 1-4 omits a return statement. Because main is a special function, return statements are optional.
Functions are blocks of code that accept any number of input objects called parameters or arguments and can return output objects to their callers.
You declare functions according to the general syntax shown in Listing 1-5.
return-type➊ function_name➋(par-type1 par_name1➌, par-type2 par_name2➍) { --snip-- return➎ return-value; }
Listing 1-5: The general syntax for a C++ function
The first part of this function declaration is the type of the return variable ➊, such as int. When the function returns a value ➎, the type of return-value must match return-type.
Then you declare the function’s name ➋ after declaring the return type. A set of parentheses following the function name contains any number of comma-separated input parameters that the function requires. Each parameter also has a type and a name.
Listing 1-5 has two parameters. The first parameter ➌ has type par-type1 and is named par_name1, and the second parameter ➍ has type par-type2 and is named par_name2. Parameters represent the objects passed into a function.
A set of braces following that list contains the function’s body. This is a compound statement that contains the function’s logic. Within this logic, the function might decide to return a value to the function’s caller. Functions that return values will have one or more return statements. Once a function returns, it stops executing, and the flow of the program returns to whatever called the function. Let’s look at an example.
For demonstration purposes, this section shows how to build a mathematical function called step_function that returns -1 for all negative arguments, 0 for a zero-valued argument, and 1 for all positive arguments. Listing 1-6 shows how you might write the step_function.
int step_function(int ➊x) { int result = 0; ➋ if (x < 0) { result = -1; ➌ } else if (x > 0) { result = 1; ➍ } return result; ➎ }
Listing 1-6: A step function that returns -1 for negative values, 0 for zero, and 1 for positive values
The step_function takes a single argument x ➊. The result variable is declared and initialized to 0 ➋. Next, the if statement sets result to -1 ➌ if x is less than 0. If x is greater than 0, the if statement sets result to 1 ➍. Finally, result is returned to the caller ➎.
To call (or invoke) a function, you use the name of the desired function, parentheses, and a comma-separated list of the required parameters. The compiler reads files from top to bottom, so the function’s declaration must appear before its point of first use.
Consider the program in Listing 1-7, which uses the step_function.
int step_function(int x) {
--snip--
}
int main() {
int value1 = step_function(100); // value1 is 1
int value2 = step_function(0); // value2 is 0
int value3 = step_function(-10); // value3 is -1
}
Listing 1-7: A program using the step_function. (This program produces no output.)
Listing 1-7 calls step_function three times with different arguments and assigns the results to the variables value1, value2, and value3.
Wouldn’t it be nice if you could print these values? Fortunately, you can use the printf function to build output from different variables. The trick is to use printf format specifiers.
In addition to printing constant strings (like Hello, world! in Listing 1-1), printf can combine multiple values into a nicely formatted string; it is a special kind of function that can take one or more arguments.
The first argument to printf is always a format string. The format string provides a template for the string to be printed, and it contains any number of special format specifiers. Format specifiers tell printf how to interpret and format the arguments following the format string. All format specifiers begin with %.
For example, the format specifier for an int is %d. Whenever printf sees a %d in the format string, it knows to expect an int argument following the format specifier. Then printf replaces the format specifier with the argument’s actual value.
NOTE
The printf function is a derivative of the writef function offered in BCPL, a defunct programming language designed by Martin Richards in 1967. Providing the specifiers %H, %I, and %O to writef resulted in hexadecimal and octal output via the functions WRITEHEX, WRITED, and WRITEOCT. It’s unclear where the %d specifier comes from (perhaps the D in WRITED?), but we’re stuck with it.
Consider the following printf call, which prints the string Ten 10, Twenty 20, Thirty 30:
printf("Ten %d➊, Twenty %d➋, Thirty %d➌", 10➍, 20➎, 30➏);
The first argument, "Ten %d, Twenty %d, Thirty %d", is the format string. Notice that there are three format specifiers %d ➊ ➋ ➌. There are also three arguments after the format string ➍ ➎ ➏. When printf builds the output, it replaces the argument at ➊ with the one at ➍, the argument at ➋ with the one at ➎, and the argument at ➌ with the one at ➏.
IOSTREAMS, PRINTF, AND INPUT OUTPUT PEDAGOGY
People have really strong opinions about which standard output method to teach C++ newcomers. One option is printf, which has a lineage that traces back to C. Another option is cout, which is part of the C++ standard library’s iostream library. This book teaches both: printf in Part I and cout in Part II. Here’s why.
This book builds your C++ knowledge brick by brick. Each chapter is designed sequentially so you don’t need a leap of faith to understand code examples. More or less, you’ll know exactly what every line does. Because printf is fairly primitive, you’ll have enough knowledge by Chapter 3 to know exactly how it works.
In contrast, cout involves a whole lot of C++ concepts, and you won’t have sufficient background to understand how it works until the end of Part I. (What’s a stream buffer? What’s operator<<? What’s a method? How does flush() work? Wait, cout flushes automatically in the destructor? What’s a destructor? What’s setf? Actually, what’s a format flag? A BitmaskType? Oh my, what’s a manipulator? And so on.)
Of course, printf has issues, and once you’ve learned cout, you should prefer it. With printf you can easily introduce mismatches between format specifiers and arguments, and this can cause strange behavior, program crashes, and even security vulnerabilities. Using cout means you don’t need format strings, so you don’t need to remember format specifiers. You’ll never get mismatches between format strings and arguments. Iostreams are also extensible, meaning you can integrate input and output functionality into your own types.
This book teaches modern C++ directly, but on this particular topic it compromises a bit of modernist dogma in exchange for a deliberate, linear approach. As an ancillary benefit, you’ll be prepared to encounter printf specifiers, which is likely to happen at some point in your programming career. Most languages, such as C, Python, Java, and Ruby, have facilities for printf specifiers, and there are analogs in C#, JavaScript, and other languages.
Let’s look at another example that uses step_function. Listing 1-8 incorporates variable declarations, function calls, and printf format specifiers.
#include <cstdio> ➊ int step_function(int x) { ➋ --snip-- } int main() { ➌ int num1 = 42; ➍ int result1 = step_function(num1); ➎ int num2 = 0; int result2 = step_function(num2); int num3 = -32767; int result3 = step_function(num3); printf("Num1: %d, Step: %d ", num1, result1); ➏ printf("Num2: %d, Step: %d ", num2, result2); printf("Num3: %d, Step: %d ", num3, result3); return 0; } -------------------------------------------------------------------------- Num1: 42, Step: 1 ➏ Num2: 0, Step: 0 Num3: -32767, Step: -1
Listing 1-8: A program that prints the results of applying step_function to several integers
Because the program uses printf, cstdio ➊ is included. The step_function ➋ is defined so you can use it later in the program, and main ➌ establishes the defined entry point.
NOTE
Some listings in this book will build on one another. To save trees, you’ll see the use of the --snip-- notation to denote no changes to the reused portion.
Inside main, you initialize a few int types, like num1 ➍. Next, you pass these variables to step_function and initialize result variables to store the returned values, like result1 ➎.
Finally, you print the returned values by invoking printf. Each invocation starts with a format string, like "Num1: %d, Step: %d " ➏. There are two %d format specifiers embedded in each format string. Per the requirements of printf, there are two parameters following the format string, num1 and result1, that correspond to these two format specifiers.
Comments are human-readable annotations that you can place into your source code. You can add comments to your code using the notation // or /**/. These symbols, // or /**/, tell the compiler to ignore everything from the first forward slash to the next newline, which means you can put comments in-line with your code as well as on their own lines:
// This comment is on its own line int the_answer = 42; // This is an in-line comment
You can use the /**/ notation to include multiline comments in your code:
/* * This is a comment * That lives on multiple lines * Don’t forget to close */
The comment starts with /* and ends with */. (The asterisks on the lines between the starting and ending forward slash are optional but are commonly used.)
When to use comments is a matter of eternal debate. Some programming luminaries suggest that code should be so expressive and self-explanatory as to render comments largely unnecessary. They might say that descriptive variable names, short functions, and good tests are usually all the documentation you need. Other programmers like to place comments all over the place.
You can cultivate your own philosophy. The compiler will totally ignore whatever you do because it never interprets comments.
One of the most important skills for a software engineer is efficient, effective debugging. Most development environments have debugging tools. On Windows, macOS, and Linux, the debugging tools are excellent. Learning to use them well is an investment that pays off very quickly. This section provides a quick tour of how to use a debugger to step through the program in Listing 1-8. You can skip to whichever environment is most relevant to you.
Visual Studio has an excellent, built-in debugger. I suggest that you debug programs in its Debug configuration. This causes the tool chain to build a target that enhances the debugging experience. The only reason to debug in Release mode is to diagnose some rare conditions that occur in Release mode but not in Debug mode.
Figure 1-4: Inserting a breakpoint
Figure 1-5: The debugger halts execution at the breakpoint.
Figure 1-6: The Autos window shows the values of variables at the current breakpoint.
You can see num1 is set to 42 and result1 is set to 1. Why does num2 have a gibberish value? Because the initialization to 0 hasn’t happened yet: it’s the next instruction to execute.
NOTE
The debugger has just emphasized a very important low-level detail: allocating an object’s storage and initializing an object’s value are two distinct steps. You’ll learn more about storage allocation and object initialization in Chapter 4.
The Visual Studio debugger supports many more features. For more information, check out the Visual Studio documentation link available at https://ccc.codes/.
Xcode also has an excellent, built-in debugger that’s completely integrated into the IDE.
Figure 1-7: Inserting a breakpoint
Figure 1-8: The debugger halts execution at the breakpoint.
Figure 1-9: The Autos window shows the values of variables at the current breakpoint.
You can see num1 is set to 42 and result1 is set to 1. Why does num2 have a gibberish value? Because the initialization to 0 hasn’t happened yet: it’s the next instruction to execute.
The Xcode debugger supports many more features. For more information, check out the Xcode documentation link at https://ccc.codes/.
The GNU Project Debugger (GDB) is a powerful debugger (https://www.gnu.org/software/gdb/). You can interact with GDB using the command line. To enable debugging support during compilation with g++ or clang++, you must add the -g flag.
Your package manager will most likely have GDB. For example, to install GDB with Advanced Package Tool (APT), enter the following command:
$ sudo apt install gdb
Clang also has an excellent debugger called the Low Level Debugger (LLDB), which you can download at https://lldb.llvm.org/. It was designed to work with the GDB commands in this section, so for brevity I won’t cover LLDB explicitly. You can debug programs compiled with GCC debug support using LLDB, and you can debug programs compiled with Clang debug support using GDB.
NOTE
Xcode uses LLDB in the background.
To debug the program in Listing 1-8 (on page 20) using GDB, follow these steps:
$ g++-8 main.cpp -o stepfun -g
$ gdb stepfun GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1 Copyright (C) 2014 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl. html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from stepfun...done. (gdb)
(gdb) break main.cpp:5
(gdb) break main
(gdb) run
Starting program: /home/josh/stepfun
Breakpoint 1, main () at main.cpp:5
5 int num1 = 42;
(gdb)
(gdb) step
6 int result1 = step_function(num1);
(gdb) step_function (x=42) at step_function.cpp:4
(gdb) finish
Run till exit from #0 step_function (x=42) at step_function.cpp:7
0x0000000000400546 in main () at main.cpp:6
6 int result1 = step_function(num1);
Value returned is $1 = 1
(gdb) next
8 int num2 = 0;
(gdb) info locals
num2 = -648029488
result2 = 32767
num1 = 42
result1 = 1
num3 = 0
result3 = 0
Notice that any variables that have not yet been initialized will not have sensible values.
(gdb) continue Continuing. Num1: 42, Step: 1 Num2: 0, Step: 0 Num3: -32768, Step: -1 [Inferior 1 (process 1322) exited normally]
GDB supports many more features. For more information, check out the documentation at https://sourceware.org/gdb/current/onlinedocs/gdb/.
This chapter got you up and running with a working C++ development environment, and you compiled your first C++ program. You learned about the components of a build tool chain and the roles they play in the compilation process. Then you explored a few essential C++ topics, such as types, declaring variables, statements, conditionals, functions, and printf. The chapter wrapped up with a tutorial on setting up a debugger and stepping through your project.
NOTE
If you have problems setting up your environment, search on your error messages online. If that fails, post your question to Stack Overflow at https://stackoverflow.com/, the C++ subreddit at https://www.reddit.com/r/cpp_questions/, or the C++ Slack channel at https://cpplang.now.sh/.
EXERCISES
Try these exercises to practice what you’ve learned in this chapter. (The book's companion code is available at https://ccc.codes.)
1-1. Create a function called absolute_value that returns the absolute value of its single argument. The absolute value of an integer x is the following: x (itself) if x is greater than or equal to 0; otherwise, it is x times −1. You can use the program in Listing 1-9 as a template:
#include <cstdio> int absolute_value(int x) { // Your code here } int main() { int my_num = -10; printf("The absolute value of %d is %d. ", my_num, absolute_value(my_num)); }
Listing 1-9: A template for a program that uses an absolute_value function
1-2. Try running your program with different values. Did you see the values you expect?
1-3. Run your program with a debugger, stepping through each instruction.
1-4. Write another function called sum that takes two int arguments and returns their sum. How can you modify the template in Listing 1-9 to test your new function?
1-5. C++ has a vibrant online community, and the internet is awash with excellent C++ related material. Investigate the CppCast podcast at http://cppcast.com/. Search for CppCon and C++Now videos available on YouTube. Add https://cppreference.com/ and http://www.cplusplus.com/ to your browser’s bookmarks.
1-6. Finally, download a copy of the International Organization for Standardization (ISO) C++ 17 Standard from https://isocpp.org/std/the-standard/. Unfortunately, the official ISO standard is copyrighted and must be purchased. Fortunately, you can download a “draft,” free of charge, that differs only cosmetically from the official version.
Note Because the ISO standard’s page numbers differ from version to version, this book will refer to specific sections using the same naming schema as the standard itself. This schema cites sections by enclosing the section name with square brackets. Subsections are appended with period separation. For example, to cite the section on the C++ Object Model, which is contained in the Introduction section, you would write [intro.object].
FURTHER READING