CodeX: C++

C++, Introduction for Entry Level Programmers is the first course in the CodeX series.

Note

Not everything in this guide is designed to make sense at first, but it will always be covered later.

This is an introductory course designed to teach entry-level programmers the basics of the C++ programming language. The course will cover topics such as basic data types and operations, conditionals, loops, functions and classes, as well as advanced concepts like pointers, memory management, threads, and RAII. Throughout the course, you will learn how to write code in C++. People taking this course should (but are not required to) have a basic understanding of fundamentals, no prior experience with C++ is necessary.


CC0
To the extent possible under law, CodeX has waived all copyright and related or neighboring rights to CodeX: C++, Introduction for Entry Level Programmers. This work is published from: United States.

Introduction to C++

C++ is like the Swiss Army Knife of programming languages! It's a powerful, versatile, and efficient language that can be used for a wide variety of applications. From creating high-performance game engines to developing sophisticated operating systems, C++ has got you covered.

What is C++ Good For?

  • Performance: When it comes to raw power, C++ is one of the top dogs in the programming world. Its ability to work close to the hardware level allows developers to create blazingly fast applications.

  • Systems Programming: Operating systems, device drivers, and embedded systems often require precise control over hardware resources. With its low-level capabilities and fine-grained memory management features, C++ excels at these tasks.

  • Game Development: The gaming industry loves C++ for its speed and flexibility. Many popular game engines like Unreal Engine and Unity support C++ for creating performance-critical components.

  • Large-scale Applications: Companies with complex software infrastructures value the scalability of C++. Its modular nature makes it easier to manage codebases with millions of lines of code.

  • Cross-platform Development: Write once, compile anywhere! Since it doesn't rely on a virtual machine or interpreter (like Java or Python), you can create native executables for different platforms without sacrificing performance.

What is C++ Not So Good For?

Despite its many strengths, there are some areas where other languages might be more suitable:

  • Rapid Prototyping: Due to its complexity and static typing system, writing code in C++ can take longer than in more dynamic languages like Python or JavaScript. If you need quick results or want to test ideas rapidly, another language might serve you better.

  • Web Development: While it's possible to write web applications in C++, other languages like JavaScript (Node.js), Ruby (Ruby on Rails), or Python (Django) offer more comprehensive libraries and frameworks for web development.

  • Beginner-friendliness: The steep learning curve of C++ can intimidate novices. For those just starting to learn programming, languages like Python or JavaScript might provide a gentler introduction.

A Simple Starting Program

Ready to dip your toes in C++? Here's a classic "Hello, World!" example:

Note

We will break down this code below in future sections.

#include <iostream>

int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

This program demonstrates the basics of C++ syntax: including the iostream library for input/output operations, defining the entry point (main function), and using the std::cout object to print "Hello, World!" to the console. Finally, we return 0, signaling successful execution.

What is C++

Note

Some of the topics on this page may be confusing depending on your level of experience; however, they will all be explained in this course.

C++ is a versatile, high-performance programming language designed for systems programming. It was developed by Bjarne Stroustrup in 1979 as an extension of the C language, with the goal of providing efficient and flexible support for object-oriented programming, low-level memory manipulation, and generic programming.

Being a statically-typed language, C++ enforces type checking at compile-time rather than run-time. This means that the programmer must explicitly define the data types of variables before using them in the program. The benefit of this approach is that it allows for better performance and helps catch errors early in the development process.

C++ offers many features that make it suitable for a wide range of applications:

  • Object-Oriented Programming (OOP): This paradigm allows you to model real-world entities as objects with properties (attributes) and behavior (methods). OOP promotes code reusability and maintainability through inheritance, encapsulation, and polymorphism.

  • Generic Programming: C++ provides powerful template support that enables you to write reusable algorithms and data structures without sacrificing performance. Templates allow you to create functions or classes that work with different data types without having to rewrite the same code for each specific type.

  • Low-level Memory Manipulation: As a systems language, C++ grants direct access to computer memory through pointers. This feature makes it possible to write highly optimized code for memory-intensive tasks or interfacing with hardware devices.

  • Standard Template Library (STL): The STL is a collection of template classes and functions provided by the C++ Standard Library. It includes containers (like vectors, lists, maps), algorithms (sort, find), iterators, and other utilities that simplify common programming tasks.

Despite its numerous advantages, there are some complexities associated with learning and mastering C++. For instance:

  1. Syntax: The syntax of C++ can be quite daunting for beginners, especially when dealing with pointers, references, and templates.

  2. Memory management: C++ requires manual memory management, which means that the programmer is responsible for allocating and deallocating memory as needed. This can lead to errors such as memory leaks or segmentation faults if not done correctly.

  3. Backward compatibility: C++ maintains a high degree (but not 100%) of backward compatibility with its predecessor, the C language. This results in a larger set of features and libraries available but also introduces some complexities when trying to understand older code or using legacy libraries. This provides the useful benefit of being able to use C code and C libraries with C++.

Key Terms

Some key terms you should know

  1. Compiler

The Compiler

When you start learning C++ programming, one term you will come across frequently is the "compiler." But what exactly is a compiler, and how does it work? This section will help you understand the role of a compiler in the C++ programming process.

What is a Compiler?

To understand the concept of a compiler, let's use an analogy. Imagine you're an author who wants to write a book. You can speak and write English fluently, but the printing press only understands a special language to print your book, you need to translate it from English to that language.

In the world of programming, the C++ language is like English, and the machine (computer) only understands a low-level language called "machine code" (the printing press language in our analogy). A compiler is a special tool that translates your C++ code into machine code, which the computer can understand and execute.

So, a compiler is a program that converts the C++ code you write into a format that the computer can understand and execute.

How Does a Compiler Work?

When you write a C++ program, you create a text file containing the C++ code. This file is called the "source code." To make your computer execute this code, you need to follow a few steps:

  1. Compilation: The compiler reads the source code, checks it for errors, and translates it into machine code.
  2. Linking: After the compilation, the machine code is combined with any additional libraries and resources needed for the program to run correctly. This process is called "linking," and it produces an "executable" file.
  3. Execution: You can now run the executable file, and the computer will execute the program according to your C++ code.

Let's go through these steps in more detail.

Compilation

When you write a C++ program, you use variables, functions, and other elements from the C++ language. The compiler's job is to take your high-level C++ code and convert it into low-level machine code that the computer can understand.

During this process, the compiler also checks your code for any syntax errors, such as missing semicolons, unmatched parentheses, or undeclared variables. If it finds any errors, it will report them, and you'll need to fix them before the compiler can successfully compile your code.

Linking

Once the compiler has translated your C++ code into machine code, it needs to combine it with any libraries or resources you've used in your program. For example, if you've used a function from the C++ Standard Library, the linker will make sure that the machine code for that function is included in the final executable.

The linker also resolves any references between different parts of your code, such as function calls or global variables. Once the linking process is complete, you'll have an executable file that contains all the necessary machine code for your program.

Execution

Now that you have an executable file, you can run it on your computer. When you execute the file, the computer will follow the instructions in the machine code, which corresponds to the C++ code you wrote. This is how your C++ program comes to life and performs the tasks you designed it to do.

There are several popular compilers available for C++ programming:

  • GNU Compiler Collection (GCC): A widely-used, open-source compiler that supports multiple languages, including C++.
  • Microsoft Visual C++ (MSVC): A compiler included with Microsoft Visual Studio, a popular integrated development environment (IDE) for Windows.
  • Clang: A compiler based on the LLVM project, known for its fast compilation speed and helpful error messages.

Each compiler may have its unique features and optimizations, but they all serve the same purpose: to convert your C++ code into machine code that your computer can understand and execute.

Development Environments

Before you dive into writing your first lines of code, it's essential to set up a development environment that will make your C++ journey enjoyable and productive.

Some features you can expect in a Development Environment are:

  • Intellisense: Intelligent code completion suggestions as you type, making it easier to write code quickly and efficiently.
  • Debugger: Debugger that can quickly identify issues in your code.
  • Code Editor: Code editor that supports syntax highlighting, making it easy to read and understand your code.
  • Version Control Integration: Git version control integration so that you can manage your project history effortlessly.

Visual Studio

Visual Studio is an excellent Integrated Development Environment (IDE) developed by Microsoft. It offers a user-friendly interface, making it one of the most popular choices among beginner programmers. It is only supported on Windows.

Installing Visual Studio IDE

In this section, we'll guide you through the simple steps of installing Visual Studio IDE on your machine. Buckle up and let's get started!

Download the Installer

First things first - head over to the Visual Studio download page and select the edition that suits you best. For beginners, we recommend Visual Studio Community, which is free for individual developers, open-source projects, academic research, education, and small professional teams.

Click on "Free download" for Visual Studio Community Edition and save the installer file on your computer.

Download Page

Run the Installer

Locate the downloaded installer file (usually in your Downloads folder) and double-click it to run. A User Account Control prompt may appear asking if you want to allow the app to make changes to your device; click "Yes."

Customize Your Installation

Once the installer starts, it'll present a list of workloads (a set of features tailored for specific development tasks). Find and select "Desktop development with C++" - this includes everything you need for C++ programming.

Feel free to explore other workloads too! If you're interested in game development or cross-platform mobile development, Visual Studio has got you covered.

After selecting workloads, click on "Install" at the bottom right corner of the installer window. Grab a cup of coffee while Visual Studio installs all necessary components!

Installer

Launch Visual Studio IDE

When the installation is complete, click on "Launch" to start Visual Studio IDE. It'll open a welcome screen where you can sign in with your Microsoft account or create one. Signing in helps synchronize your settings and preferences across devices.

Launch

Create Your First C++ Project

Congratulations! You've successfully installed Visual Studio IDE, and now it's time to create your first C++ project:

  1. Click on "Create a new project" from the Welcome screen.
  2. Choose "Empty Project" under the "C++" filter, then click "Next."
  3. Give your project a name and choose a location to save it.
  4. Click on "Create."

New Project

You're all set! Now go ahead and write your first lines of C++ code in the Source Files folder by creating a new .cpp file. The world of programming awaits!

Basic Concepts

In this section, you will be introduced to some fundamental concepts in C++

  1. Data Types
  2. Variables
  3. Strings and Arrays
  4. Constants and Literals
  5. Input/Output
  6. Statements and Expressions
  7. The Main Function

Comments in C++

Comments are an essential part of programming, as they allow you to insert notes, explanations or reminders directly into your code. In C++, there are two types of comments: single-line and multi-line comments.

Single-line comments

Single-line comments begin with two forward slashes (//) and continue until the end of the line. Any text following the // on the same line will be considered a comment and will not be executed by the compiler.

Example

int main() {
    // This is a single-line comment
    return 0;
}

Multi-line comments

Multi-line comments begin with a forward slash followed by an asterisk (/*) and end with an asterisk followed by a forward slash (*/). Any text between these delimiters will be considered a comment, even if it spans multiple lines.

Example

int main() {
    /*
    * This is a multi-line comment.
    * It can span multiple lines.
    */
    return 0;
}

Good commenting practices make your code more readable and maintainable, so always remember to include meaningful comments when writing your programs.

Data Types

Imagine you're a chef in the kitchen, and you have many ingredients to cook with. These ingredients are like the basic building blocks for your dish. Similarly, in C++, data types are the basic building blocks for your program. They are the way we represent different kinds of data in our code, like numbers, characters, and strings (text).

Fundamental Data Types

C++ provides several fundamental data types that can be used to represent different kinds of data. These data types can be broadly categorized into the following groups:

  1. Integer types: These data types are used to represent integer values (whole numbers). Some examples are int, short int, long int, and long long int.
  2. Floating-point types: These data types are used to represent real numbers (numbers with a fractional/decimal part). Examples include float and double.
  3. Character types: These data types are used to represent single characters, like 'a' or '7'. The most common character type is char.
  4. Boolean type: This data type is used to represent true or false values. It is called bool.

Note

Each of these types has an unsigned version (e.g., unsigned int) that can only store positive numbers and zero. The range of unsigned types is double that of their signed counterparts because they don't need to store negative values.

Integer Types

Think of integer types as the different sizes of measuring cups. They can hold varying amounts of data (or numbers). The most commonly used integer types in C++ are:

  • short int: Can store small integer values, typically between -32,768 and 32,767.
  • int: Can store a wide range of integer values, typically between -2,147,483,648 and 2,147,483,647.
  • long int: Can store large integer values, typically between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807.
  • long long int: Can store very large integer values, with at least the same range as long.

Floating-Point Types

Floating-point types are like measuring cups with a built-in scale – they can hold both whole numbers and decimals. C++ provides two floating-point types:

  • float: Can store single-precision floating-point numbers (about 7 decimal digits of accuracy).
  • double: Can store double-precision floating-point numbers (about 15 decimal digits of accuracy).
  • long double: Can store very large double-precision floating-point values.

double provides greater precision than float, but it also consumes more memory.

Character Types

Character types are like individual letters or symbols that you can use to form words and sentences. In C++, the most common character type is char. It can store a single character, like A, z, or #.

Characters in C++ are encoded using the ASCII (American Standard Code for Information Interchange) system, which assigns a unique number to each character. This means that, under the hood, a char is actually an integer type that can store values between 0 and 255.

Boolean Type

The boolean type is like a simple light switch – it can be either on (true) or off (false). In C++, the boolean type is called bool.

bool variables can only store two values: true or false. They are commonly used to represent the outcome of a condition or to control the flow of a program.

Pointer Types

In C++, pointers are a special type of data type that can store the memory address of another variable. They provide a way to indirectly access and manipulate the value stored at that memory address.

A pointer is declared by specifying the data type it points to, followed by an asterisk (*).

It is important to note that the data type specified when declaring a pointer is not the type of the pointer itself but rather the type of data it will point to. This means that int* is a pointer that can store the address of an int variable, while char* can store the address of a char variable.

We will discuss these more in a later chapter

Strings and Arrays

Arrays are a list type that stores multiples of the same item. For example, strings are an array of char which you would use for storing words or sentances. This will be explored in a later chapter, but it is reccomended to read variables first.

Variables

Think of a variable as a container or a box that can store a specific type of data. This data can be a number (integer or decimal), a character, or any other data type. In C++, variables are used to store and manipulate data that can be used in various parts of a program.

Let's use an analogy to understand variables better. Imagine you are running a store, and you have several shelves to store different types of items. Each shelf is labeled with a unique name, and you can only store items of a specific type on that shelf. In the world of programming, each shelf represents a variable, the unique name is the variable's name, and the type of items that can be stored represents the data type of that variable.

Declaring Variables

Before we can use a variable in a C++ program, we need to declare it. Declaring a variable involves specifying its data type and giving it a name. The general syntax for declaring a variable is as follows:

data_type variable_name;

Data types have been explained the data types section, so you should have a basic understanding of them. Here's a quick example of declaring an integer variable called age:

int age;

In this example, int is the data type, and age is the variable name. This declaration tells the compiler that we have created a variable named age that can store integer values.

You can also declare multiple variables of the same data type on a single line by separating them with commas:

int x, y, z;

This line declares three integer variables: x, y, and z.

Assigning Values to Variables

After declaring a variable, we can assign a value to it using the assignment operator =. Here's an example of assigning the value 25 to the previously declared age variable:

age = 25;

Now the variable age contains the value 25. It's also possible to declare a variable and assign a value to it in the same line:

int age = 25;

This line declares an integer variable called age and assigns the value 25 to it.

Using Variables

Once a variable has been declared and assigned a value, you can use it in various ways in your program. For example, you can perform calculations with it, use it in conditional statements, or display its value on the screen.

Here's an example of using the age variable to calculate the number of days someone has lived:

int age = 25;
int days_lived = age * 365;

Note

As you can see above, you can combine the declaration and assignment in one line.

In this example, we multiply the value of age by 365 to calculate the number of days someone has lived and store the result in a new variable called days_lived.

Strings and Arrays

Arrays allow you to store multiple elements of the same data type together, while strings are a special kind of array that stores multiple characters.

Arrays

Think of an array as a row of storage lockers, where each locker can store an item of the same type. An array is a collection of elements of the same data type, stored together in a continuous (sequential) block of memory. You can access and modify individual elements in an array using their index, which represents their position in the array.

Declaring an Array

To declare an array in C++, you need to specify its data type, followed by its name and the size of the array in square brackets. For example, to declare an integer array of size 5, you can write:

int myArray[5];

This creates an array named myArray that can store 5 integer values.

Accessing Array Elements

To access an element in an array, you can use its index. Array indices in C++ start at 0, so the first element is at index 0, the second element is at index 1, and so on. For example, to assign a value to the first element of myArray, you can write:

myArray[0] = 42;

To access the value stored in the first element of myArray, you can use:

int firstValue = myArray[0];

Array Initialization

You can initialize an array when you declare it by specifying its elements inside curly braces. For example, to declare and initialize an integer array, you can write:

int myArray[] = {1, 2, 3, 4, 5};

In this case, you don't need to specify the size of the array, as the compiler will automatically determine it based on the number of elements you've provided.

Strings

Strings are sequences of characters used to represent text. In C++, strings are essentially arrays of characters, terminated by a special character called the null terminator (\0). The null terminator indicates the end of the string.

Declaring and Initializing Strings

You can declare and initialize a string in C++ using either an array of characters or the std::string class provided by the C++ Standard Library. Here are two ways to declare and initialize a string:

// Using a character array
char myString[] = "Hello, world!";

// Using the std::string class
#include <string>
std::string myString = "Hello, world!";

The std::string class provides many useful functions for working with strings, such as finding the length of a string, concatenating strings, and comparing strings. It is generally recommended to use std::string over character arrays when working with strings in C++.

Accessing String Characters

You can access individual characters in a string using the same indexing method as arrays. For example, to access the first character of a std::string, you can write:

char firstChar = myString[0];

Constants and Literals

Constants

Constants are like unwavering pillars that hold up your code structure. They are fixed values that never change throughout the execution of a program. Once you declare a constant, it remains steady and immovable.

You might wonder why we need constants when we have variables. Well, imagine if all the pillars in your building were made out of shape-shifting material! Sounds chaotic, right? In programming too, sometimes you want to ensure that certain values remain steadfast and unaffected by any changes during runtime.

To create a constant in C++, you use the const keyword before declaring a variable:

const int speedOfLight = 299792;

Now you can be confident that speedOfLight will always remain constant at 299792 km/s throughout your program!

Literals

Literals are like the most basic units in our construction analogy - think of them as individual bricks or planks of wood. They represent fixed values directly written into your code without any accompanying names or identifiers.

Literals can be classified into several categories based on their data types:

  1. Integer literals: These are whole numbers without any decimal points or fractional parts.

    42    // An integer literal (the meaning of life)
    -100  // Another integer literal
    
  2. Floating-point literals: These represent real numbers with decimal points or exponents.

    3.14        // A floating-point literal
    -0.001      // Another floating-point literal
    6.022e23    // Scientific notation for large/small numbers (Avogadro's number!)
    
  3. Character literals: These are single characters enclosed within single quotes.

    'A'     // A character literal
    '9'     // Another character literal (yes, even digits can be characters!)
    '\n'    // An escape sequence representing a newline character
    
  4. String literals: These are sequences of characters enclosed within double quotes.

    "Hello, World!"          // A string literal
    "C++ is awesome!"        // Another string literal
    "I'm Craving nachos\n"   // String literals can also contain escape sequences!
    
  5. Boolean literals: There are only two Boolean literals: true and false.

    true     // A Boolean literal representing truthiness!
    false    // A Boolean literal representing falseness :(
    

Warning

Depending on your experience level, the last two below may not make any sense. We will talk more about this in future chapters.

  1. Pointer literals: A pointer literal represents a null pointer value, which is a special value indicating that the pointer isn't pointing to any valid memory location. This will be explored more in future chapters.

    nullptr    // A pointer literal in C++11 and later
    NULL       // A pointer literal in older C++ versions (defined as 0)
    
  2. User-defined literals: Starting from C++11, you can create your own custom literals using user-defined literal operators.

    #include <iostream>
    
    constexpr long double operator"" _cm(long double x) { return x * 10; }
    constexpr long double operator"" _m(long double x) { return x * 1000; }
    
    int main() {
      long double height = 3.4_cm; // User-defined literal for centimeters
      long double length = 1.2_m;  // User-defined literal for meters
    
      std::cout << "Height: " << height << " mm" << std::endl;
      std::cout << "Length: " << length << " mm" << std::endl;
    
      return 0;
    }
    

    In this example, we've defined two user-defined literals _cm and _m to convert distances into millimeters.

Input/Output

Streams: The Rivers of Data

Imagine your program as an island surrounded by streams (rivers) that bring data to it and take data away from it. These rivers are like communication channels between your program and the outer world, including devices such as keyboards, monitors, files, and more! In C++, these "rivers" are called streams.

There are three main types of streams:

  1. Input Stream (istream) - Brings data into your program from various sources.
  2. Output Stream (ostream) - Takes data from your program and sends it to different destinations.
  3. I/O Stream (iostream) - A combination of input and output streams for bidirectional communication.

The Journey Begins: iostream

Before we dive into the fun part (coding), let's understand some essential tools for I/O operations in C++. We need two magical keys from the iostream library:

  • cin: Our trusty friend for reading input data.
  • cout: Our reliable companion for displaying output data.

You may have seen this in previous chapters.

To include these keys in our program, simply add this line at the beginning:

#include <iostream>

Talking to Your Program: Reading Input

Let's start by making our program ask for our name! To do that, we use cin, followed by the extraction operator (>>).

Here's how you can read a user's name:

#include <iostream>
#include <string>

int main() {
    std::string name;
    
    std::cout << "What is your name? ";
    std::cin >> name;

    return 0;
}

In this example, we first declared a variable name of type std::string. Then, using cin, we read the input and stored it in the name variable.

Sharing Your Thoughts: Displaying Output

Now that we have our user's name, let's greet them! This time, we'll use cout followed by the insertion operator (<<).

Here's how you can display a greeting message:

#include <iostream>
#include <string>

int main() {
    std::string name;
    
    std::cout << "What is your name? ";
    std::cin >> name;

    std::cout << "Hello, " << name << "! Nice to meet you!" << std::endl;

    return 0;
}

In this example, we used cout to display a greeting message containing the user's name. We also added std::endl, which creates a newline character, making our output more organized.

Putting It All Together: Let's Chat!

Congratulations! You've learned the basics of Input/Output in C++. As a reward for your efforts, let's create a mini-chat program where users can send messages and receive automatic responses:

#include <iostream>
#include <string>

int main() {
    std::string username;
    std::string message;

    // Introduce ourselves and ask for their username
    std::cout << "Welcome to C++ Chatbot!" << std::endl;
    std::cout << "Please enter your username: ";
    std::cin >> username;

    // Start the chat loop
    while (true) {
        // Read user's message
        std::cout << username << ": ";
        std::cin.ignore();
        std::getline(std::cin, message);

        // Exit the chat if they type "bye"
        if (message == "bye") {
            break;
        }

        // Generate an automatic response
        std::cout << "Chatbot: I hear you, " << username << "! You said: \"" 
                  << message << "\"." << std::endl;
    }

    return 0;
}

And there you have it! A simple chatbot using C++.

The Main Function in C++

In C++, the main function is the starting point of your program. It is where your program begins execution. Every C++ program must have a main function, as it is the first function that gets executed when your program runs. The main function is responsible for managing the flow of your program and calling other functions as needed.

Let's look at a simple C++ main function:

main.cpp

#include <iostream>

int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

In this example, the main function consists of several parts:

  1. #include <iostream>: This is a preprocessor directive that tells the compiler to include the iostream header file. The iostream header file is part of the C++ Standard Library and is required when you want to use input and output (I/O) operations, such as reading from the keyboard or writing to the screen.

  2. int main(): This is the main function itself. It is a function named main, and it returns an int (integer) value. When you see int main(), it means that the main function takes no arguments and returns an integer. The parentheses () indicate that this is a function, and the curly braces {} define the body of the function.

  3. std::cout << "Hello, World!" << std::endl;: This is a statement that outputs the string "Hello, World!" to the console. std::cout is an object that represents the standard output stream (usually the console). The << operator is used to send data to the output stream. In this case, we are sending the string "Hello, World!" followed by a newline, represented by std::endl. The std:: part is a namespace specifier that tells the compiler to look for cout and endl in the std namespace, which is part of the C++ Standard Library.

    • The << is an example of an operator. Operators are special symbols that represent specific operations, such as arithmetic, comparison, or assignment. In this case, the << operator is known as the stream insertion operator. It is used to insert data, such as a string, into an output stream.

      You could use regular function calls to achieve the same result. In this case, you would replace the << operator with the put and write member functions of std::cout. Here's an example:

      std::cout.write("Hello, World!", 13);
      std::cout.put('\n');
      

      This code snippet uses the write function to output the "Hello, World!" string and the put function to output the newline character. Note that the write function requires the length of the string as its second argument.

  4. return 0;: This is a return statement that indicates the end of the main function. The value 0 is returned to the operating system, signifying that the program has executed successfully. A non-zero value would indicate an error.

Statements and Expressions in C++

In C++ programming, statements and expressions are the building blocks of your code.

Expressions

An expression is a piece of code that evaluates or calculates a value. It consists of variables, constants, and operators (+, -, *, /, etc.) combined in a meaningful way. Think of expressions as a simple math equation or a question that the program needs to answer.

Here are some examples of expressions:

5 + 3 // add
x * y // multiply
a > b // comparison

Statements

A statement, on the other hand, is a complete line of code that performs an action. It usually consists of one or more expressions joined together. Statements end with a semicolon (;), which tells the compiler that the line is complete.

Here are some examples of statements:

int x = 5; // Declaration and assignment statement
x = x + 3; // Assignment statement
std::cout << x; // Output statement (or function call)

Types of Statements

Note

Control Statements and Functions will be discussed further in the next two chapters.

C++ has several types of statements, including:

  1. Declaration Statements: These statements declare and define variables and their types. For example:

    int age; // Declares an integer variable named 'age'
    std::string name; // Declares a string variable named 'name'
    
  2. Assignment Statements: These statements assign a value to a variable. For example:

    age = 25;
    name = "John Doe";
    
  3. Control Statements: These statements control the flow of your program, such as loops and conditional statements. For example:

    if (age > 18) {
        std::cout << "You are an adult!";
    } else {
        std::cout << "You are a child!";
    }
    
    // Subtract age until it is no longer greater than 0.
    while (number > 0) {
        std::cout << number << std::endl;
        number--; // Equivalent to `number = number - 1;`
    }
    
  4. Function Call Statements: These statements call or invoke a function in your program. For example:

    int result = add_numbers(5, 3); // Calls the 'add_numbers' function with arguments 5 and 3
    

Combining Statements and Expressions

In C++ programs, you'll often see expressions and statements combined. For example:

int x = 5; // Declaration and assignment statement
int y = x * 2 + 1; // Declaration, assignment, and expression combined

In this example, the expression x * 2 + 1 is combined with the declaration and assignment statement to create a new statement.

Control Structure

Control structures are fundamental building blocks in programming that help manage the flow of execution in a program. They allow you to create complex logic and decision-making capabilities by selectively executing code based on specific conditions or repetitions. The main elements of control structures include:

  1. Sequence: This is the default control structure where code is executed line by line in the order it appears.

  2. Flow Control: These statements enable you to execute specific code blocks depending on whether certain conditions are true or false.

  3. Jump Statements: Jump statements provide additional control over the flow of your program by transferring execution to another part of your code, skipping loop iterations, or ending loops prematurely.

Flow Control

Flow control in programs refers to the various methods used to manage the order and execution of code based on certain conditions or repetitions. Key elements of flow control include:

  1. Conditional Statements: These include if, if-else, and switch statements, which allow you to execute specific code blocks based on conditions being true or false.

  2. Loops: These include for, while, and do-while loops, which enable you to execute a block of code repeatedly based on a specific condition or a predetermined number of iterations.

Conditional Statements

These allow you to control the flow of your program based on certain conditions. With conditional statements, you can add logic and decision-making capabilities to your code.

What are Conditional Statements?

Conditional statements are used to decide whether a specific block of code should be executed or not, depending on whether a certain condition is true or false. They form the backbone of many algorithms and programs.

In C++, we mainly use three types of conditional statements:

  1. if statement
  2. if-else statement
  3. switch statement

Let's dive into each one in more detail!

The if Statement

The simplest type of conditional statement is the if statement. It checks if a condition is true, and if so, executes the code inside its block.

Example

#include <iostream>

int main() {
    int age = 17;

    if (age >= 18) {
        std::cout << "You are eligible to vote!";
    }

    return 0;
}

In this case, since age is less than 18, nothing will be printed.

The if-else Statement

The if-else statement adds another branch for when the condition is false.

Example

#include <iostream>

int main() {
    int age = 17;

    if (age >= 18) {
        std::cout << "You are eligible to vote!";
    } else {
        std::cout << "You are not eligible to vote yet.";
    }

    return 0;
}

In this case, since age is less than 18, the message "You are not eligible to vote yet." will be printed.

The switch Statement

The switch statement is used when you need to make a decision based on multiple discrete values of a variable. It's a cleaner alternative to using multiple nested if-else statements.

Example

#include <iostream>

int main() {
    int day = 3;

    switch (day) {
        case 1:
            std::cout << "Monday";
            break;
        case 2:
            std::cout << "Tuesday";
            break;
        case 3:
            std::cout << "Wednesday";
            break;
        default:
            std::cout << "Invalid day number!";
    }

    return 0;
}

In this case, since day is equal to 3, the output will be "Wednesday".

That's it! You can start adding logic and decision-making capabilities to your programs!

Loops

Loops allow you to execute a block of code repeatedly, making them perfect for repetitive tasks and reducing code duplication.

In C++, we mainly use three types of loops:

  1. for loop
  2. while loop
  3. do-while loop

Let's explore each one in more detail!

The for Loop

The for loop is ideal when you know how many times you want the loop to run. Its syntax includes an initialization, a condition, and an update statement.

Example

#include <iostream>

int main() {
    for (int i = 0; i < 5; i++) {
        std::cout << "Iteration: " << i << std::endl;
    }
}

This loop will print the numbers from 0 to 4. The variable i is initialized with a value of 0, and as long as i < 5, the code inside the loop will execute, incrementing i by one after each iteration (using i++).

The while Loop

The while loop is great when you don't know how many times the loop should run, but you do have a condition that determines when it should stop.

Example

#include <iostream>

int main() {
    int number = 1;

    while (number <= 10) {
        std::cout << "Number: " << number << std::endl;
        number++;
    }
    
    return 0;
}

This loop prints the numbers from 1 to 10. As long as the condition number <= 10 is true, the loop will keep executing and incrementing number.

The do-while Loop

The do-while loop is similar to the while loop, but with one key difference: it always executes the code inside the loop at least once, even if the condition is false from the start.

Example

#include <iostream>

int main() {
    int number;

    do {
        std::cout << "Enter a number between 1 and 10: ";
        std::cin >> number;
    } while (number < 1 || number > 10);

    std::cout << "You entered a valid number: " << number << std::endl;

    return 0;
}

In this example, we prompt the user for a number between 1 and 10. If they enter an invalid value, we continue asking until they provide a valid one. The code inside the loop will always execute at least once, ensuring that we have a valid input before moving on.

Go ahead and experiment with different types of loops to find out which one works best for your specific needs.

Jump Statements

Jump statements allow you to control the flow of your program by transferring execution to another part of your code. They are especially useful when working with loops and conditional statements.

In C++, we have four main types of jump statements:

  1. break
  2. continue
  3. return
  4. goto

Let's jump into each one in more detail!

The break Statement

The break statement is used to exit a loop or a switch statement prematurely, skipping any remaining iterations or cases.

Example

#include <iostream>

int main() {
    for (int i = 0; i < 10; i++) {
        if (i == 5) {
            break;
        }
        std::cout << "Iteration: " << i << std::endl;
    }

    return 0;
}

In this example, the loop will only print the numbers from 0 to 4. When i reaches 5, the break statement will be executed, and the loop will terminate immediately.

The continue Statement

The continue statement is used to skip the current iteration of a loop and move on to the next one, ignoring any code that comes after it in the loop body.

Example

#include <iostream>

int main() {
    for (int i = 0; i < 10; i++) {
        if (i % 2 == 1) {
            continue;
        }

        std::cout << "Even number: " << i <<std::endl;
    }

    return 0;
}

In this example, we're printing even numbers between 0 and 9. When encountering an odd number (i % 2 ==1), the continue statement skips the rest of the code inside the loop and proceeds directly to the next iteration.

The return Statement

The return statement is used to exit a function and return a value to the caller. It can also be used to jump out of loops and conditional statements within a function. You will become more familiar with return in the next section.

The goto Statement

The goto statement is used to transfer control to another part of your code. While it can be useful in some cases, it's generally discouraged due to its potential to make code harder to read and maintain.

Example

#include <iostream>

int main() {
    int count = 0;

start:
    count++;
    std::cout << "Iteration: " << count << std::endl;
  
    if (count < 5) {
        goto start;
    }

    std::cout << "Finished!" << std::endl;
  
    return 0;
}

In this example, the goto statement transfers control back to the start: label after each iteration until count reaches 5. It's essentially creating a loop without using regular loop constructs.

While jumps can be powerful tools when used effectively, remember that readability and maintainability should always be prioritized when writing code.

Functions in C++

In C++, functions are blocks of code that perform a specific task and can be called (or invoked) from other parts of the program. Functions make code easier to understand, maintain, and reuse by breaking the program into smaller, modular components.

Terminology

Function definition: A function definition is a block of code that defines the function's behavior, including its input parameters, output, and the actual code that will be executed when the function is called.

Function declaration: A function declaration is a statement that provides information about the function like its name, return type, and input parameters, but not the actual implementation. It serves as an interface between the function definition and the code that calls the function.

Function call (or invocation): A function call is a statement in the program that requests the execution of a specific function. When a function is called, the control is transferred to the function definition, and after the function has finished executing, the control is returned to the point in the program where the function was called.

Parameters (or arguments): Parameters, also known as arguments, are the input values that a function receives when it is called. They are declared within the parentheses in the function definition and function declaration.

Return type: The return type is the data type of the value that a function returns after its execution. If a function does not return any value, its return type is void.

Syntax

Here is the basic syntax for declaring and defining a function in C++:

// Function declaration (prototype)
return_type function_name(parameter_type parameter1, parameter_type parameter2);

// Function definition
return_type function_name(parameter_type parameter1, parameter_type parameter2) {
    // Function body (code to be executed)
    ...
    return value; // Optional, depending on the return type
}

Example

Example

#include <iostream>

// Function declaration
int sum(int a, int b);

int main() {
    int x = 5, y = 10;
    int result = sum(x, y); // Function call
    std::cout << "The sum is: " << result << std::endl;
    return 0;
}

// Function definition
int sum(int a, int b) {
    int total = a + b; // Function body (code to be executed)
    return total; // Return the result
}

In this example, we declare a function called sum with a return type of int and two input parameters of type int. The function definition provides the actual implementation of the sum function, which calculates the sum of the input parameters and returns the result. The sum function is called from the main function, and the result is printed to the console.

Key Points

  • Functions help to modularize and reuse code, making it easier to understand and maintain.
  • Functions have a name, return type, and input parameters.
  • Functions are declared (prototyped) before they are called and defined separately.
  • The return type of a function indicates the type of value it returns after its execution. If a function does not return any value, its return type is void.
  • Function calls transfer control to the function definition and then return control to the point in the program where the function was called.

Header Files

While learning C++, you'll often come across the term "header files." In this section, we will discuss what header files are, why they are essential, and how to use them effectively in your C++ programs.

What are header files?

Header files are simply text files with the extension .h or .hpp that contain declarations of functions, classes, variables, constants, and other elements that you want to share across multiple source files (.cpp files) in your program. They allow you to separate the implementation (the actual code) from the interface (the declarations) of your program.

In other words, header files act as an interface that tells the compiler what functions, classes, and other elements are available for use without actually providing the implementation. This makes your code more organized, modular, and easier to maintain.

Why are header files important?

Header files play a crucial role in C++ programming for several reasons:

  1. Modularity: By separating the declarations and implementations, you can create more maintainable and reusable code. Other developers can easily understand and use your code by looking at the header files without going through the entire implementation.

  2. Code Sharing: Header files allow you to share code between multiple source files, reducing code duplication and making your program more efficient.

  3. Easier Compilation: When you modify a source file, only that file needs to be recompiled, not the entire program. If you didn't use header files, any change to a function or class would require recompiling every file that uses it.

How to use header files in C++?

To use header files in your C++ program, you need to follow these steps:

  1. Create a header file: Create a new text file with the extension .h or .hpp. You can use any text editor to create a header file.

  2. Add declarations: Add the declarations of the functions, classes, variables, and other elements that you want to share across multiple source files in your program.

  3. Include the header file: In the source files (.cpp files) where you want to use the elements declared in the header file, use the #include directive followed by the name of the header file enclosed in double quotes (") or angle brackets (< >). For example:

    #include "my_header_file.h"
    

    or

    #include <my_header_file.hpp>
    

    The double quotes are used for including custom header files that you create, while the angle brackets are mainly used for including standard library header files.

  4. Implement the declared elements: In a separate source file (.cpp file), provide the implementation for the elements declared in the header file.

Note

The #include directive copy and pastes the header file into your source file (.cpp file). You may have issues if you end up having the same header file included more than once. To circumvent this, put #pragma once at the top of your header file.

Example

Example

my_math_functions.h (header file)

#pragma once

// Function declarations
int add(int a, int b);
int subtract(int a, int b);

my_math_functions.cpp (source file with implementation)

#include "my_math_functions.h"

// Function implementations
int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

main.cpp (source file that uses the functions)

#include <iostream>
#include "my_math_functions.h"

int main() {
    int a = 10, b = 5;
    std::cout << "Sum: " << add(a, b) << std::endl;
    std::cout << "Difference: " << subtract(a, b) << std::endl;
    return 0;
}

In this example, we have created a header file my_math_functions.h that contains the declarations for two functions add and subtract. We include this header file in both the implementation file my_math_functions.cpp and the main.cpp file, which uses the functions.

By using header files, we can separate the declarations and implementations of the functions, making our code more organized and easier to maintain.

Function Overloading

Function overloading is a concept in C++ that allows you to define multiple functions with the same name but with different parameter lists. This means that you can have several versions of a function, each performing a slightly different operation based on the number and types of parameters passed. This not only improves code readability but also makes it more organized and easier to maintain.

Let's explore this fantastic feature with some examples!

Why Do We Need Function Overloading?

Imagine you're working on a project where you need to find the sum of two numbers. Simple, right? Now, what if these numbers can be integers or doubles? You'd have to write two separate functions for each case: one for int and another for double. But what if there are more types involved? That's when function overloading is useful.

With function overloading, you can define multiple functions with the same name but different parameter lists, allowing them to work seamlessly together.

How Does It Work?

Function overloading works by letting the compiler choose the most suitable version of an overloaded function based on how it's called. The compiler does this by matching the number and types of arguments provided during a call with those specified in any available overloaded functions.

Example

#include <iostream>

// Function overload #1 - Sum of two integers
int sum(int a, int b) {
    return a + b;
}

// Function overload #2 - Sum of two doubles
double sum(double a, double b) {
    return a + b;
}

int main() {
    int result_int = sum(5, 6);         // Calls the first overload
    double result_double = sum(3.2, 4.1); // Calls the second overload

    std::cout << "Sum of integers: " << result_int << std::endl;
    std::cout << "Sum of doubles: " << result_double << std::endl;

    return 0;
}

In this example, we defined two overloaded sum functions. The first one takes two int parameters while the second one takes two double parameters. When we call these functions with integer or double values, the compiler automatically selects the appropriate version based on the argument types.

Best Practices

While function overloading can be incredibly useful, there are some best practices you should follow:

  1. Use descriptive parameter names: This makes it easier to understand what each parameter does and helps avoid confusion.
  2. Maintain consistency in parameter order: Keeping a consistent order of parameters across different overloads will make your code much more readable and manageable.
  3. Avoid ambiguous overloads: Sometimes, an ambiguous situation may arise when multiple overloads could match a given call. In such cases, the compiler cannot determine which version to use and will throw an error.

Conclusion

Function overloading is an awesome feature in C++ that allows you to define multiple versions of a function with different parameter lists. It improves code readability and organization by allowing you to write cleaner and more efficient code.

Recursion

Recursion occurs when a function calls itself directly or indirectly. It's like inception but for functions!

Factorial illustration

A good example for this is the factorial function.

flowchart LR
    A["Start"] --> B["Function: factorial(n)"]
    B --> C{Is n <= 1?}
    C -->|Yes| D["Return 1 + sum"]
    C -->|No| E["Call factorial(n-1)"]
    E --> B

Imagine you're climbing a staircase, and at each step, you can either take one or two steps. How many ways are there to reach the top? This problem can be elegantly solved using recursion!

Here's how we might write this function in C++:

int countWays(int n) {
    // Base case: If there are no more steps left, we found one way!
    if (n == 0) return 1;

    // Base case: If there are negative steps left, there's no valid way.
    if (n < 0) return 0;

    // Recursive case: Count ways by taking one step or two steps.
    return countWays(n - 1) + countWays(n - 2);
}

With iteration

int countWays(int n) {
    if (n == 0) return 1;
    if (n < 0) return 0;
    
    int a = 1, b = 0, temp;
    
    for (int i = 1; i <= n; ++i) {
        temp = a;
        a = a + b;
        b = temp;
    }
    
    return a;
}

The Anatomy of a Recursive Function

A successful recursive function must have two essential parts:

  1. Base Case(s): A condition where the function stops calling itself and returns a value directly. These cases prevent infinite loops and provide concrete solutions for the smallest possible sub-problems.
  2. Recursive Case(s): A condition where the function calls itself with modified arguments to solve smaller instances of the problem.

Let's dissect our countWays function to see these parts in action:

  • Base Cases:

    • if (n == 0) return 1;: If there are no more steps left, we found one way.
    • if (n < 0) return 0;: If there are negative steps left, there's no valid way.
  • Recursive Case:

    • return countWays(n - 1) + countWays(n - 2);: Count ways by taking one step or two steps. Notice how the problem size decreases with each call!

Recursion vs. Iteration

Recursion and iteration (loops) can often be used interchangeably to solve problems. While recursion may provide a more elegant and intuitive solution, it can also lead to performance issues if not implemented carefully.

In some cases, recursion may cause a significant amount of overhead due to function calls, resulting in a slower solution compared to an iterative approach. However, this can often be mitigated through optimization techniques like memoization.

Pointers and References

Pointers and references are a fundamental concept in C++ programming, and they play a crucial role in understanding how memory works within the language. Although pointers may seem intimidating at first, they are essential for writing efficient and flexible code.

Pointer Basics

Pointers are a fundamental concept in C++ and they allow us to access memory directly. They can be a bit confusing at first, but once you understand the basics, they become an invaluable tool in your programming toolbox.

What is a Pointer?

A pointer is a variable that stores the memory address of another variable. In other words, it "points" to the location of another variable in memory. You can think of a pointer as an arrow that points to a specific memory location.

Let's use an analogy to help visualize this concept. Imagine you have several boxes lined up, each containing a piece of data (a number or a character). Each box has its own unique address, just like houses on a street. A pointer is like giving someone directions to one of these boxes by telling them the box's address.

Pointer declaration in C++

int* ptr;

This declares a pointer ptr that will point to an integer variable (int). The asterisk (*) before the variable name indicates that it's a pointer.

How to Use Pointers

To use pointers effectively, we need to understand two important operators: the address-of operator (&) and the dereference operator (*).

Address-of Operator (&)

Address-of Operator

int  number = 42;
int* ptr = &number;

In this code snippet, we create an integer variable number and assign it the value 42. We then create an integer pointer ptr and assign it the address of number. Now, our pointer ptr is pointing to the memory location where number is stored.

Dereference Operator (*)

The dereference operator (*) allows you to access or modify the value stored at the memory address pointed to by a pointer. For example:

Dereference Operator

#include <iostream>

int main() {
    int  number = 42;
    int* ptr = &number;

    std::cout << *ptr; // Output: 42
    return 0;
}

In this example, when we use *ptr, we are essentially saying, "give me the value stored at the address that ptr is pointing to." Since ptr is pointing to number, using *ptr gives us the value of number.

Combining these operators.

To access memory by using its pointer, you can use both the dereference operator and the address-of operator.

Modifying the value at a pointer

You can also modify the value stored at the memory location by using the dereference operator

#include <iostream>

int main() {
    int  number = 42;
    int* ptr = &number;

    *ptr = 10;

    std::cout << number; // Output: 10
    return 0;
}

In this case, we used *ptr to change the value of number indirectly through its memory address. We are not modifying ptr itself, rather the value it points to.

Conclusion

Pointers are a powerful feature in C++ that allows for direct manipulation of memory addresses. They can be tricky at first but understanding how pointers work will help you write more efficient and flexible programs. Remember these key concepts:

  • Pointers store memory addresses.
  • Use the address-of operator (&) to get a variable's memory address.
  • Use the dereference operator (*) to access or modify the value at a memory address.

Arrays and Pointers

In C++, arrays and pointers are closely related, as they both deal with memory addresses. An array is a contiguous block of memory that stores multiple elements of the same data type, while a pointer is a variable that holds the address of another variable or an array element.

Iterating through arrays

Calculating the size of an array

In C++, sizeof(arr) / sizeof(arr[0]) is a common method to calculate the number of elements in an array.

sizeof(arr) returns the total size (in bytes) of the array, while sizeof(arr[0]) returns the size (in bytes) of a single element in the array.

By dividing the total size of the array by the size of a single element, you can obtain the number of elements in that array.

In C++, you can iterate through an arbitrary sized array using a for loop:

#include <iostream>

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int size = sizeof(arr) / sizeof(arr[0]); // Calculate the size of the array

    for (int i = 0; i < size; ++i) {
        std::cout << arr[i] << " ";
    }

    return 0;
}

Relationship between Arrays and Pointers

Let's assume we have an integer array arr:

int arr[5] = {1, 2, 3, 4, 5};

The name of the array arr itself points to the base address (i.e., starting address) of the array. This means that you can use a pointer to access the elements of this array.

To understand this better, consider an analogy where each element in the array is a room in a hotel. The hotel represents our memory, and each room (element) has its own unique room number (memory address). The name arr acts like the hotel's entrance - it tells us where the first room starts.

Now let's create an integer pointer p and assign it the base address of our arr.

int* p = arr;

Here, we created a pointer p that points to the first element (room) in our array (hotel). Now we can use this pointer to access individual elements in the array just like using an index

Full Example

Putting it together

#include <iostream>

int main() {
    int  arr[5] = {1, 2, 3, 4, 5};
    int* p = arr;

    std::cout << "First element: " << *p << std::endl;        // Output: First element: 1
    std::cout << "Second element: " << *(p + 1) << std::endl; // Output: Second element: 2

    return 0;
}

Notice how we used arithmetic operations on pointers (+1 for accessing next element). This brings us to our next topic, Pointer Arithmetic

Pointer Arithmetic

Pointer arithmetic allows you to perform operations on pointers to manipulate memory addresses directly. Although it may seem complicated at first, understanding the basics of pointer arithmetic can greatly improve your ability to work with arrays and memory in C++. In this section, we will discuss the basics of pointer arithmetic and provide some examples to help you understand the concept.

What is Pointer Arithmetic?

In simple terms, pointer arithmetic is performing mathematical operations (addition or subtraction) on pointers. When you perform these operations on pointers, you're effectively changing the memory address the pointer points to.

Let's use an analogy to better understand this concept: Imagine that a street has houses numbered sequentially (e.g., 1001, 1002, 1003...). If you're standing in front of house number 1001 and take one step forward (pointer + 1), you'll be in front of house number 1002; if you take one step back (pointer - 1), you'll be back at house number 1000.

Similarly, when we add or subtract from a pointer, we're moving forward or backward in memory addresses.

Note

It's important to note that when performing pointer arithmetic, adding or subtracting a value n from a pointer will actually change its address by n * sizeof(type) bytes. This means that if your pointer is pointing to an integer (int*), and sizeof(int) is typically 4 bytes, adding 1 will increase the address by 4 bytes.

Basic Pointer Arithmetic Operations

Here are some basic operations with examples:

Addition

You can add an integer value to a pointer:

int arr[] = {10, 20, 30};
int* ptr = arr; // Pointing to the first element of arr

ptr = ptr + 2; // Now pointing to the third element of arr (arr[2])

Subtraction

You can subtract an integer value from a pointer:

int arr[] = {10, 20, 30};
int* ptr = arr + 2; // Pointing to the third element of arr

ptr = ptr - 1; // Now pointing to the second element of arr (arr[1])

Pointer Difference

You can find the difference between two pointers of the same type:

int arr[] = {10, 20, 30};
int* start_ptr = &arr[0]; // Pointing to the first element of arr
int* end_ptr = &arr[2];   // Pointing to the third element of arr

ptrdiff_t diff = end_ptr - start_ptr; // diff is now equal to 2

What is ptrdiff_t?

ptrdiff_t type is a base signed integer type of C/C++ language. The type's size is chosen so that it could store the maximum size of a theoretically possible array of any type. On a 32-bit system ptrdiff_t will take 32 bits, on a 64-bit one 64 bits. Pointers are integers and their sizes depend on your system, similarly, ptrdiff_t's size also depends on your system.

Why Use Pointer Arithmetic?

One common use case for pointer arithmetic is working with arrays. Since an array in C++ is stored as a contiguous block of memory, you can use pointer arithmetic to iterate through it and access its elements more efficiently than using indexing.

Iterating through an array using pointer arithmetic

#include <iostream>

int main() {
    int arr[] = {10, 20, 30};
    int* ptr;

    for (ptr = &arr[0]; ptr != &arr[3]; ++ptr) {
        std::cout << *ptr << " ";
    }

    return 0;
}

In this example, we initialize ptr with the address of the first element in arr. Then, we loop until ptr reaches one past the last element (&arr[3]). Inside the loop, we print out each element by dereferencing ptr.

Understanding and utilizing pointer arithmetic in your programs will help you manage memory more effectively and write efficient code when dealing with arrays or other data structures.

References

References are a convenient feature that allows us to create an alias for a variable. In simple terms, you can think of a reference as another name for an existing variable. When we declare a reference, it must be initialized with a variable, and once initialized, we cannot change the reference to refer to another variable.

Why use references?

References provide an alternative to pointers when working with variables indirectly. Unlike pointers, references are safer because they cannot be nullptr and always refer to a valid object or variable. Additionally, the syntax for using references is cleaner and easier to read compared to pointers.

Here’s an analogy that might help you understand the concept of references better: Imagine you have two names (say "Alice" and "Bob") which both refer to the same person. If someone asks you about Alice or Bob's age, it doesn't matter which name they use because they're referring to the same person. Similarly, in C++, if you have a variable x and its reference y, both can be used interchangeably while accessing or modifying data as they point to the same memory location.

How do we declare and use references?

To declare a reference, put an ampersand (&) before the reference name during declaration followed by equal sign (=) and then mention the variable whose alias it will be:

int  x = 42;
int& y = x; // y is now referencing x

Now y is a reference (alias) for x. Any operation we perform on y will affect x

Putting it all together

#include <iostream>

int main() {
    int  x = 42;
    int& y = x; // y is now referencing x

    y = 10; // This assigns 10 to x through y.
    std::cout << "Value of x: " << x << std::endl; // Output: Value of x: 10
    std::cout << "Value of y: " << y << std::endl; // Output: Value of y: 10

    return 0;
}

References as function arguments

One common use case for references is in function arguments. By passing a reference to a function, we can modify the original variable directly without the need for pointers or returning a new value.

Example

#include <iostream>

void increment(int& num) {
    // This increments the variable that was passed in as an argument, not a copy.
    num++;
}

int main() {
    int x = 5;
    increment(x); // Pass x by reference to the increment function.
    std::cout << "Value of x after increment: " << x << std::endl; // Output: Value of x after increment: 6

    return 0;
}

In this example, we pass x by reference to the increment function. Since it's passed by reference, any changes made inside the increment function will affect our original variable x.

Try removing the reference in increment's argument list, instead of passing by reference this will pass by copy and the original value will not be changed.

Remember that references must be initialized when declared and cannot be changed to refer to another object later. In other words, a reference always refers to the object with which it was initialized.

Data Structures

Data structures are specialized formats for efficiently organizing, storing, and managing data. They allow us to perform a variety of data operations such as insertion, deletion, searching, and sorting. Understanding and selecting the appropriate data structure for a given task can greatly improve the performance of your programs. This section will go over some of the most important data structures in C++.

Multi-Dimensional Arrays

In C++, multi-dimensional arrays are basically arrays of arrays. They are used to hold structured data, such as a table with rows and columns. A common example is an array that depicts a matrix, with multiple rows and columns of data.

Two-Dimensional Arrays

The two-dimensional array (2D array) is the most simple type of multi-dimensional array. Consider it a grid or a table with rows and sections. You must indicate the number of rows and columns when declaring a 2D array:

int myArray[3][4]; // This declares an integer 2D array with 3 rows and 4 columns

Each element in the 2D array can be accessed using its row and column index:

myArray[0][0] = 1; // This sets the first row, first column element to the value 1

You can also initialize a 2D array when declaring it:

// This initializes the elements of our 2D array
int myArray[3][4] = {
    {1, 2,  3,  4},
    {5, 6,  7,  8},
    {9, 10, 11, 12}
};

Multi-Dimensional Arrays

C++ allows you to declare arrays with more than two dimensions by simply adding more square brackets [].

Declaring a three-dimensional (3D) array

int my3dArray[2][3][4];

This declares an integer 3D array with dimensions

  • "depth" or "layers" (size: 2)
  • "rows" (size: 3)
  • "columns" (size: 4)

A 3D array can be thought of as a collection of tables or matrices. In this case, we have two layers, each with a table with three rows and four columns.

Just like with 2D arrays, you can access and modify the elements using their indices:

// This sets the first layer, second row, third column element to the value 42
my3dArray[0][1][2] = 42;

You can initialize a multi-dimensional array when declaring it:

// This initializes the elements of 3D array
int my3dArray[2][3][4] = {
    {
        {1, 2,  3,  4},
        {5, 6,  7,  8},
        {9, 10, 11, 12}
    },
    {
        {13, 14, 15, 16},
        {17, 18, 19, 20},
        {21, 22, 23, 24}
    }
};

Looping over Multi-Dimensional Arrays

To iterate over all elements in a multi-dimensional array, you can use nested loops. The outer loop iterates over one dimension (e.g., rows), while an inner loop iterates over another (e.g., columns)

Example

#include <iostream>

int main() {
    // Print all elements in a given two-dimensional integer array:
    int myArray[3][4] = {
        {1, 2,  3,  4},
        {5, 6,  7,  8},
        {9, 10, 11, 12}
    };

    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 4; ++j) {
            std::cout << "Element at [" << i << "][" << j << "] is: " << myArray[i][j] << std::endl;
        }
    }
}

The same concept applies to arrays with more dimensions, simply add more nested loops as needed.

Conclusion

Multi-dimensional arrays make it easy to work with structured data in C++. Using indices and loops, they are simple to access and manipulate. However, larger arrays may consume significant memory resources, so when working with multi-dimensional arrays, it is important to consider the size and efficiency.

Intro to Classes and Methods

Warning

Classes will not fully be described here but other sections in the Data Structures chapter will be better understood. This will be fully explained in a future chapter.

Introduction to Classes

In C++, a class is a user-defined data type that groups variables (known as attributes) and functions (called methods) under a single name. It serves as a blueprint for creating objects, which are instances of the class.

Think of a class like a template for a cookie-cutter: you can create many cookies (objects) with the same shape (attributes) and actions (methods).

Attributes

Attributes are variables defined within a class. They represent the properties or actions of an object created from the class. For example, if we have a Car class, some possible attributes could be color, brand, and speed.

Methods

Methods are functions defined within a class. They usually represent actions that can be performed by objects created from the class. Continuing with our Car example, some possible methods could be accelerate(), brake(), and changeGear().

Strings & String Functions

std::string is a powerful class in the C++ Standard Library that represents a sequence of characters. It's an alternative to C-style character arrays (like char[]) and provides many helpful functions for string manipulation.

To use std::string, you need to include the <string> header file:

#include <iostream>
#include <string>

int main() {
    std::string hello = "Hello world!";
    std::cout << hello << std::endl;

    return 0;
}

Custom Operators

In C++, custom operators, also known as operator overloading, allow us to define how built-in operators like +, -, *, /, and [] will work with our user-defined types. Simply put, we can create our own implementation of these operators to make them behave the way we want when used with our custom objects.

In the std::string class, you may see custom operators. In the upcoming sections, we will dive deeper into the concept of custom operators and explore how to implement them in various scenarios.

Common String Methods

Length and Capacity

Length or Size: length() or size() returns the number of characters in the string.

#include <iostream>
#include <string>

int main() {
    std::string s = "hello";
    std::cout << s.length();  // Output: 5

    return 0;
}

Empty: empty() returns true if the string has no characters, otherwise false.

#include <iostream>
#include <string>

int main() {
    std::string emptyString = "";
    if (emptyString.empty()) {
        std::cout << "The string is empty!" << std::endl;
    }

    return 0;
}

Accessing Characters

Indexing characters: at(index) or operator[](index) returns a reference to the character at a given position. Indices start from 0, just like arrays.

#include <iostream>
#include <string>

int main() {
    std::string str = "hello";
    char first = str.at(0);        // 'h'
    char second = str.operator[](1); // 'e'
    char third = str[2];             // 'l'

    std::cout << first << std::endl;
    std::cout << second << std::endl;
    std::cout << third << std::endl;

    return 0;
}

Note: Using an index out of bounds will result in undefined behavior.

Modifying Strings

Appending strings: append(str) or operator+=(str) appends another string or character to the end of the current string.

#include <iostream>
#include <string>

int main() {
    std::string greeting = "Hello ";
    greeting.append("world!");          // "Hello world!"
    greeting += "! How are you today?"; // "Hello world! How are you today?"

    std::cout << greeting << std::endl;

    return 0;
}

Inserting characters: insert(pos, str) inserts a string or character at a specified position.

#include <iostream>
#include <string>

int main() {
    std::string message = "We love C";
    message.insert(7, "++"); // "We love C++"

    std::cout << message << std::endl;

    return 0;
}

Erasing characters: erase(pos, count) Erases count characters from the position pos. If count is not provided, it erases all characters until the end of the string.

#include <iostream>
#include <string>

int main() {
    std::string text = "I like bananas";
    text.erase(7, 8); // "I like"

    std::cout << text << std::endl;

    return 0;
}

Searching and Comparing

Finding occurrences: find(str) Searches for the first occurrence of a substring and returns its starting position. If not found, it returns the std::string::npos constant (or -1).

#include <iostream>
#include <string>

int main() {
    std::string sentence = "C++ is fun!";
    size_t pos = sentence.find("fun");
    if (pos != std::string::npos) {
        std::cout << "Found 'fun' at position: " << pos << std::endl;
    }

    return 0;
}

Comparing strings: compare(str) Compares two strings. Returns 0 if they're equal, a positive value if the first string is greater than the second one, and a negative value otherwise.

#include <iostream>
#include <string>

int main() {
    std::string s1 = "apple", s2 = "banana";
    int result = s1.compare(s2);
    if (result == 0) {
        std::cout << "s1 equals s2" << std::endl;
    } else if (result > 0) {
        std::cout << "s1 is greater than s2" << std::endl;
    } else {
        std::cout << "s1 is less than s2" << std::endl;
    }

    return 0;
}

Conclusion

std::string is a versatile and easy-to-use class for string manipulation in C++. It provides many functions to access, modify, search, and compare strings. Using std::string instead of C-style character arrays can make your code safer and more readable.

Vectors