C++ Nibbles

Introduction

You know when you take a small bite of a burger. That’s called a nibble. Just a wee little bite. That is the theme of this series. Bite-sized C++ knowledge. Enjoy!

Each Nibble will be:

  • Less than 3 minutes reading time
  • Concise (That’s the entire point of a “nibble”).

  • #000 – How C++ actually runs

    Before we write a single line of C++, it’s important to understand how our programs get executed, because they are ultimately executed on hardware. However, a computer’s CPU doesn’t understand C++ so … what happens? Here’s a flowchart explaining what happens to the code we write. A translation of our C++ source code into machine…


  • #001 – C++ as a high-level language

    #000 – How C++ actually runs spoke about how our C++ code gets converted into machine code, allowing our programs to execute on hardware. Low-level languages operate directly with hardware. This includes registers and the CPU’s Instruction Set (ISA). Thus, a programmer must be familiar with these domains to … program. Conversely, high-level languages abstract…


  • #002 – The history of C++

    I believe understanding the history of C++ allows us to examine why it was created and how it expands on its predecessor, C because C++ was ultimately developed to serve a purpose. Introducing C The C language was developed in 1972 primarily as a systems programming language by Dennis Ritchie at Bell Telephone laboratories (a…


  • #003 – What is C++ used for?

    C++ excels in situations where high performance and precise control over memory and other resources is needed. Applications include: C++ also has a large number of high-quality 3rd party libraries available, which can shorten development times significantly. With an update every 3 years, a large number of 3rd party libraries and its widespread use in…


  • #004 – How do we develop programs? Part 1

    Now, I’m sure you’re itching to develop your first programs. However, it save us alot of headache, it’s wise to discuss the workflow of programming because ultimately, the program exists to solve a problem. Let’s make that step 1: Here is the complete flowchart. Let’s analyse it step by step Step 1 – Scrutinize the…


  • #005 – How do we develop programs? Part 2

    Here’s the flowchart from the previous nibble, we let off from Step 4. Step 4 – Compile the program The compiler performs two functions (get it functions): Step 5 – Link object files When we create C++ programs, we split it into numerous files. This heaps with readability and organization. We then reference other files…


  • #006 – Build configurations

    A build configuration are the settings the IDE uses to build your project. The build configuration typically includes things like what the executable will be named, what directories the IDE will look in for other code and library files, whether to keep or strip out debugging information, how much to have the compiler optimize your…


  • #007 – Warnings and error levels

    When the compiler encounters an issue it will emit a diagnostic message or diagnostic for short. The C++ standard does not define how diagnostic messages should be categorized, worded, or how those issues should affect the compilation of the program. However, compilers have adopted the following convention: In the spirit of building robust programs, it’s…


  • #008 – Choosing your language standard

    As part of configuring our compiler, we can select the language standard or version. Recall that various versions exist including C++98, C++03, C++11, C++14, C++17, C++20, C++23. Which language standard should you choose? Whilst you might think to choose the latest standard, it’s recommended to choose a version that is 1-2 versions older than the…


  • #009 – How to use comments

    I’m a big advocate of comments. It conveys the programmer’s intent, which is very useful if you’re interpreting someone else’s code. How to use comments A comment should communicate what a function or library does. For example: A programmer should read the comment and get an idea of what the program does without interpreting a…


  • #010 – The modern way of variable initialization

    Defining a variable is called definition. Giving a variable a value is called assignment. Initialization merges these two instructions into one statement. E.g. int x { 11 }; This is the modern way of variable initialization. The historical way is via copy-initialization. E.g. int x = 11; Copy-initialization has lost support in modern C++ due…


  • #011 – Using [[maybe_unused]] to suppress compiler warnings

    If a variable is instantiated but not used, the compiler throws a warning. If you don’t like to remove it, C++17 introduced the [[maybe_unused]] keyword. This tells the compiler to not freak out when it sees an unused variable. It suppresses compiler warnings. There is no performance impact.


  • #012 – Prefer ‘\n’ over std::endl

    When using std::cout we typically return a new line at the end of each statement for cleanliness. Despite std::endl being designed to serve this purpose, it is more efficient to use '\n'. This is because the later doesn’t flush the buffer after each std::cout. Doing this repeatedly is wasteful, inefficient and unnecessary. Additionally, C++’s output…


  • #013 – Avoid default initialization for variables

    Consider the following This is default-initialization. This defines the variable with a garbage value (not zero). If we print its contents, it will be whatever value is sitting at that memory address. Who knows! When you don’t initialize a variable, C++ populates it with whatever contents that memory address holds. These are called garbage values.…


  • #014 – Chaining operators

    Consider the following: This statement contains multiple << operators that are chained in a single instruction. The insertion operator (<<) operators on a C-style string literal, an escape sequence, a class object and a function. The answer comes down to two C++ features working together in perfect unison. 1. What do operators return? When we…


  • #015 – C++ does not support nested functions

    Consider the following: According to the rules of C++, nested functions are illegal. Functions must be implemented in the global namespace. Then they can be called in other functions. Why is this the case? This behaviour was inherited from C. The justification was to simplify compilers, abide to memory limitations (back when computers were slow)…


  • #016 – Using function calls as arguments

    Consider this program The return value of GetRadius() is stored in a variable and passed into CalculateArea(). x only exists act as the interface between the two functions. We can do better. Doing Better We merge these two instructions in one line. The return value of getRadius() is used as the argument in calculateArea(). This…


  • #017 – Crash Course on Local Variables

    Introduction A local variable in C++ is any variable defined within a function (or block). This definition is type-agnostic—it does not matter whether the variable is a fundamental type (int, double), a standard library type (std::string, std::unique_ptr), or a user-defined type (class, struct). If it is declared inside a function, it is local. Thus, despite…


  • #018 – Two reasons to use forward declarations

    Consider the following: In main(), we call the function before its implementation. This is possible since we included a forward declaration before the function call. This tells the compiler: Hey, just letting you know this function exists. However, I’ll implement it later For advanced functions, you don’t want to include the function implementation before main()…


  • #019 – The std namespace

    Introduction The entire C++ standard library lives inside a single namespace called std, which acts as one large container holding all of its functionality. The standard library is the result of decades of work by some of the best engineers in the industry. The classes, containers, algorithms, and methods it provides are battle-tested, highly optimized,…


  • #020 – Conditional Debugging with Preprocessor Directives

    As part of the debugging process, we want to isolate the troublesome section of code. We can do this by sprinkling std::cerr statements to validate that certain parts of the code are working. However, after you’re finished with them you need to not only remove them but remember to remove them. We can do better.…


  • #021 – Thinking in memory. What a variable ACTUALLY is

    Recap Literals are hardcoded values such as 5 or "Hello world!". They cannot be changed after runtime. A variable also holds a value. But not quite. A variable is a representation of a value that is stored at a memory address. It represents a memory location where the value is stored, NOT the value itself.…


  • #022 – Using void as a function parameter

    void is a special type. It means no type! We are accustomed to functions returning void. However, a function parameter can also be void. This means the function accepts zero parameters. Why is this possible? This feature is inherited from C for backwards compatibility reasons. In C, an empty parameter list in the function declaration…


  • #023 – How big is an int?

    In #021 – Thinking in memory. What a variable ACTUALLY is, we established that variables are simply representations of values that are stored in memory locations. When you initialize a variable int y {11};, the compiler reserves a portion of memory in bytes. How this is done is abstracted from the programmer. We don’t need…


  • #024 – The dangers of using unsigned ints

    Unsigned int‘s are strictly positive. It gets double the positive range than its signed counterpart. It seems like a free upgrade. However, unsigned integers introduce a class of bugs that are easy to write, difficult to detect, and silently destructive. Google’s C++ style guide goes as far as advising engineers to avoid them entirely outside…


  • #025 – Representing numbers in scientific notation. Part 1 – Planning

    Introduction The mass of the Earth is 5,972,200,000,000,000,000,000,000kg. How on Earth do you interpret that? (See what I did there). As an alternative, we can display it as 5.9722 x 10²⁴kg. This is scientific notation. It’s used to display very very very large numbers and very very very small numbers. The syntax is: significand *…


  • #027 – Floats or doubles?

    Introduction Integers are great and all but if you want to store numbers with a fractional component. This is often what you see in the real world. Temperature, distance, currency all contain a fractional component. In these applications, you must use the floating point type. Types of floats For this article, I’ll be discussing the…


  • #028 – Make all of your variables const (if possible)

    Why variables should be made constant If a variable can be made constant, it generally should be made constant. Every moving part in a system increases complexity and the risk of defect or failure. Non-constant variables are moving parts, while constant variables are not. Applying const Getters are easy candidates. In the example below, const…


  • #029 – Using literal suffixes

    Introduction Consider the following, Woah! Even though we didn’t specify the type of the variable, the compiler was able to deduce it. When auto is used, the compiler deduces the variable’s type from the initializer. Since literals already have types, this instruction succeeded. How is this possible? This is possible since literals have a type.…


  • #030 – Printing integers in binary

    Consider the following: Hey! What happened? We assigned binary values to the variable however std::cout printed their decimal representation. C++ allows us to initialize a variable with any numbering system All of these values are equivalent. However, the variable doesn’t record if its decimal, hexadecimal, binary or octal. How std::cout behaves std::cout defaults to printing…


  • #031 – std::string_view: A Lightweight String Reference

    Consider this program Here’s what happens This is additional overhead for simply getting a string. This is inefficient. Fortunately, we can do better. Since we don’t modify the string, this is a good justification to use std::string_view. Introducing std::string_view (C++17) std::string_view provides read-only access to a: Let’s refactor the above program using std::string_view Did you…


  • #032 – Why function arguments may not evaluate left-to-right

    Consider the following, The result isn’t 5 ?? cal should print 2 + (3 * 1) = 5. What’s going on? You’re assuming the function arguments gets evaluated left-to-right, cal(2, 3, 1). However, the first argument doesn’t necessarily have to be in the first argument position. Same for the 2nd and 3rd argument. Thus, calculate(getValue(),…


  • #033 – prefix vs postfix increment/decrement operators

    Consider Seems fairly harmless. Despite achieving the same result (incrementing the operand), the prefix and postfix versions have slight differences. In terms of the loop variable i, does it matter if we do i++ vs ++i? Prefix increment Prefix increment increments the variable first, then evaluates to the updated value. Standard stuff. This is what…


  • #034 – The conditional operator is CHEESE

    Conditional operator allows us to do this: and this: Introduction The conditional operator (?:) is also called the arithmetic if operator. It’s a ternary operator, meaning it requires 3 operands. Historically, it’s been C++’s only ternary operator. It’s concise way of implementing an if/else block. Syntax: condition ? true expression : false expression Uses of…