Literals

Literals are fixed values written directly in your code. When you type a number, character, or text string into your program, you're using a literal.

int totalScore { 100 };         // 100 is an integer literal
bool isGameActive { true };      // true is a boolean literal
double temperature { 98.6 };     // 98.6 is a double literal
std::cout << "Welcome Player!";  // "Welcome Player!" is a C-style string literal

Literals are also known as literal constants because their values cannot change. The number 100 always represents one hundred, regardless of where it appears in your code.

Understanding literal types

Every literal in C++ has a specific type, determined automatically by the compiler based on how the literal is written.

By default, the compiler assigns types as follows:

Literal value Examples Default type Notes
integer value 100, 0, -42 int
boolean value true, false bool
floating point value 98.6, 0.0, 2.5 double (not float!)
character 'x', '\t' char
C-style string "Welcome Player!" const char[17] see C-style string literals section

Changing literal types with suffixes

Sometimes you need a literal to have a different type than the default. You can accomplish this by adding a suffix to the literal.

Here are the most commonly used suffixes:

Data type Suffix Meaning
integral u or U unsigned int
integral l or L long
integral ul, uL, Ul, UL, lu, lU, Lu, LU unsigned long
integral ll or LL long long
integral ull, uLL, Ull, ULL, llu, llU, LLu, LLU unsigned long long
integral z or Z The signed version of std::size_t (C++23)
integral uz, uZ, Uz, UZ, zu, zU, Zu, ZU std::size_t (C++23)
floating point f or F float
floating point l or L long double
string s std::string
string sv std::string_view

In most situations, suffixes aren't required (with the notable exception of f for floats).

Related content: The s and sv suffixes need an extra line of code to work. We'll explore these in the Introduction to std::string and Introduction to std::string_view lessons.

Suffix case sensitivity

Most suffixes work in either uppercase or lowercase, with these exceptions:

  • s and sv must be lowercase
  • When using double l or L, both characters must have matching case (lL and Ll are invalid)

Since lowercase L can look like the digit 1 in many fonts, many programmers prefer uppercase L.

Best Practice
Prefer uppercase L suffix over lowercase l.

Integer literals in action

Here's how to use suffixes with integer literals:

#include <iostream>

int main()
{
    std::cout << 42 << '\n';   // 42 (no suffix) is type int (by default)
    std::cout << 42L << '\n';  // 42L is type long
    std::cout << 42u << '\n';  // 42u is type unsigned int

    return 0;
}

Usually, plain int literals work fine even when initializing non-int types:

int main()
{
    int level { 10 };           // ok: types match
    unsigned int health { 100 }; // ok: compiler converts int 100 to unsigned int 100
    long experience { 5000 };    // ok: compiler converts int 5000 to long 5000

    return 0;
}

The compiler automatically converts the int literal to match the target variable's type.

Floating point literals

By default, floating point literals are type double. To create float literals, add the f (or F) suffix:

#include <iostream>

int main()
{
    std::cout << 3.14 << '\n';  // 3.14 (no suffix) is type double (by default)
    std::cout << 3.14f << '\n'; // 3.14f is type float

    return 0;
}

Many beginners get confused by this compiler warning:

float pi { 3.14 }; // warning: 3.14 is a double literal, not a float literal

The literal 3.14 without a suffix is type double, not float. The compiler doesn't consider what you're doing with the literal when determining its type. Since double doesn't match float, the value must be converted, potentially losing precision. Hence the warning.

Fix this in one of two ways:

float pi { 3.14f };     // use 'f' suffix so literal matches variable type
double pi { 3.14 };     // change variable to double to match literal type

Scientific notation for floating point literals

Floating point literals can be written in two ways:

  1. Standard notation uses a decimal point:
double pi { 3.14159 };        // standard notation
double temperature { -40.5 }; // can be negative
double zero { 0.0 };          // prefer 0.0 over 0. for clarity
  1. Scientific notation uses e to represent the exponent:
double earthMass { 5.97e24 };        // 5.97 x 10^24
double electronCharge { -1.6e-19 };  // -1.6 x 10^-19

Working with string literals

In programming, a string is a sequence of characters representing text, like player names, messages, or commands.

Here's a basic C++ program using a string literal:

#include <iostream>

int main()
{
    std::cout << "Welcome Player!";
    return 0;
}

"Welcome Player!" is a string literal. String literals use double quotes (unlike character literals which use single quotes).

C++ doesn't include strings as a fundamental type. Instead, it inherited C strings (or C-style strings) from the C language, which are complex and difficult to work with.

Two important facts about C-style string literals:

  1. All C-style string literals have an implicit null terminator. The string "hello" actually has six characters: 'h', 'e', 'l', 'l', 'o', and '\0' (ASCII code 0). This trailing '\0' character is the null terminator, marking where the string ends. Strings ending with a null terminator are called null-terminated strings.

For advanced readers: This explains why "Welcome Player!" has type const char[17] rather than const char[16] - the hidden null terminator counts as a character.

  1. Unlike most literals (which are values), C-style string literals are const objects created when the program starts and guaranteed to exist throughout program execution. In contrast, std::string and std::string_view literals create temporary objects that must be used immediately before they're destroyed.

Avoiding magic numbers

A magic number is a literal with unclear meaning or one that might need changing later.

Consider these examples:

const int maxPlayers { numberOfTeams * 5 };
setLimit(5);

What does 5 mean? In the first line, you might guess it's players per team, but it's not obvious. In the second line, the meaning is completely unclear.

Magic numbers create problems when values need updating. If teams grow from 5 to 6 players, which 5s should change? You'd need to examine every occurrence of 5 in your code to determine if it relates to team size. This is time-consuming and error-prone.

Use symbolic constants instead:

const int playersPerTeam { 5 };
const int maxPlayers { numberOfTeams * playersPerTeam }; // meaning is now clear

const int maxUsernameLength { 20 };
setLimit(maxUsernameLength); // clearly different from playersPerTeam

Now the context is clear, and you only need to update values in one place.

Note that magic numbers aren't always numeric:

int main()
{
    printWelcome("BattleQuest"); // bad: game name might be used elsewhere or change
}

Some literal values are obvious in context and aren't considered magic:

int playerId { 0 };       // ok: clearly starting at 0
playerId = playerId + 1;  // ok: obviously incrementing

Other numbers might be self-explanatory:

int milesToKilometers(int miles)
{
    return miles * 1609; // ok: clearly a conversion factor
}

Sequential numbers used as identifiers typically aren't magic:

int main()
{
    // ok: these are just sequential identifiers
    displayCharacter(1); // naming this character1 wouldn't add clarity
    displayCharacter(2);
}
Best Practice
Avoid magic numbers in your code (use constexpr variables instead).

Summary

  • Literals are fixed values written directly in your code that cannot change
  • Default types: Integer literals are int, floating point literals are double, boolean literals are bool, and character literals are char
  • Suffixes let you change a literal's type (e.g., 42u for unsigned int, 3.14f for float)
  • Floating point literals: By default are type double, use the f suffix to create float literals
  • Scientific notation: Use e for exponents (e.g., 5.97e24 represents 5.97 × 10²⁴)
  • String literals: Use double quotes for C-style string literals, which include a hidden null terminator
  • Magic numbers: Avoid using literals with unclear meaning or values that might change - use named constants instead
  • Best practices: Use uppercase L for long literals, always use f suffix for float literals, and replace magic numbers with symbolic constants

Understanding literals is fundamental to C++ programming. Using appropriate literal types and avoiding magic numbers makes your code clearer and easier to maintain.