Skip to content

C++ Constants/Literals

Literals (often called constants) are fixed values directly embedded in the source code. They represent specific data like numbers, characters, or strings.

Constants, in a broader sense, are identifiers (like variables) whose values cannot be changed after initialization. Modern C++ offers several ways to define constants.

C++ supports various types of literals:

Represent whole numbers. They can be specified in different bases:

  • Decimal (base 10): Default, no prefix (e.g., 123, -45).
  • Octal (base 8): Prefix 0 (e.g., 0173 which is 123 in decimal). Avoid leading zeros unless octal is intended, as it can be confusing.
  • Hexadecimal (base 16): Prefix 0x or 0X (e.g., 0x7B or 0X7b, both are 123 in decimal). Uses digits 0-9 and letters a-f (or A-F).
  • Binary (base 2) (C++14): Prefix 0b or 0B (e.g., 0b01111011 which is 123 in decimal).

Integer literals can have suffixes to specify their type:

  • u or U: unsigned (e.g., 123u).
  • l or L: long (e.g., 123L).
  • ll or LL (C++11): long long (e.g., 123LL).
  • Combinations are possible (e.g., 123UL, 45ull).

C++14 introduced digit separators (single quotes ') for readability:

long long big_number = 1'000'000'000LL; // Easier to read
int binary_flags = 0b1101'0010'1010'0001;

Represent numbers with a fractional part. They can be written in decimal or exponential notation:

  • Decimal: Must include a decimal point (.) or an exponent (e.g., 3.14159, 12.0, .5, 5.).
  • Exponential: Uses e or E followed by an optional sign and an integer exponent (power of 10) (e.g., 6.022e23, 1.6E-19).
  • Hexadecimal floating-point literals (using 0x prefix and p for exponent) exist but are less common (0x1.921fb5p+1).

Suffixes specify the type:

  • No suffix: double (default).
  • f or F: float (e.g., 3.14f).
  • l or L: long double (e.g., 3.1415926535L).

Digit separators (') can also be used (C++14).

double pi = 3.141'592'653'589'793;

Represent truth values:

  • true: Represents the boolean value true.
  • false: Represents the boolean value false.

Represent single characters, enclosed in single quotes (').

  • Plain char: 'a', '?', '7'. Type is char.
  • Wide Character: L'β' (prefix L). Type is wchar_t.
  • UTF-8 (C++11): u8'貓' (prefix u8). Type is char (C++11/17) or char8_t (C++20).
  • UTF-16 (C++11): u'Ω' (prefix u). Type is char16_t.
  • UTF-32 (C++11): U'🌍' (prefix U). Type is char32_t.

Escape Sequences: Special characters represented using a backslash (\):

Escape SequenceMeaning
\Backslash (\) character
’Single quote (') character
”Double quote (") character
?Question mark (?) character
\nNewline
\tHorizontal tab
\vVertical tab
\bBackspace
\rCarriage return
\fForm feed
\aAlert (bell)
\0Null character
\oooOctal value (up to 3 digits)
\xhh…Hexadecimal value
\uHHHH (C++11)Universal character name (Unicode - 16-bit)
\UHHHHHHHH (C++11)Universal character name (Unicode - 32-bit)

Represent sequences of characters, enclosed in double quotes ("). They are implicitly null-terminated (a \0 character is automatically appended).

  • Plain: "Hello, World!" (Type is const char[]).
  • Wide: L"你好" (prefix L). Type is const wchar_t[].
  • UTF-8 (C++11): u8"κόσμε" (prefix u8). Type is const char[] (C++11/17) or const char8_t[] (C++20).
  • UTF-16 (C++11): u"नमस्ते" (prefix u). Type is const char16_t[].
  • UTF-32 (C++11): U"שלום" (prefix U). Type is const char32_t[].
  • Raw String Literals (C++11): R"(No\escape\needed)" or R"delimiter(Text)delimiter". Useful for strings containing many backslashes or quotes.

Adjacent string literals are concatenated by the compiler:

std::cout << "This is part 1, " "and this is part 2.\n";

nullptr (C++11): Represents the null pointer value. It is type-safe and preferred over 0 or NULL for representing null pointers.

Constants are named entities whose values cannot change after initialization. Modern C++ offers two primary ways:

The const qualifier declares an object as immutable. Its value must be initialized upon declaration and cannot be modified later. const variables have scope like regular variables.

#include <iostream>
int main() {
const int MAX_USERS = 100;
const double PI = 3.14159;
const char NEWLINE = '\n';
// MAX_USERS = 200; // Error! Cannot assign to const variable.
double circumference = 2 * PI * 5.0; // Use the constant
std::cout << "Max users: " << MAX_USERS << NEWLINE;
std::cout << "Circumference: " << circumference << NEWLINE;
return 0;
}

const is preferred over #define for defining constants because it is type-safe and respects scope.

constexpr declares that an expression can be evaluated at compile time. When applied to a variable, it implies const and requires the initializer to be a compile-time constant expression.

constexpr allows constants to be used in contexts requiring compile-time evaluation, such as array bounds (before C++11 VLAs), template arguments, and enum initializers.

#include <array> // For std::array
constexpr int get_array_size() {
return 5 * 2;
}
int main() {
constexpr int COMPILED_SIZE = 10;
constexpr double GRAVITY = 9.80665;
constexpr int DYNAMIC_SIZE = get_array_size(); // Evaluated at compile time
int myArray[COMPILED_SIZE]; // OK: COMPILED_SIZE is compile-time constant
std::array<double, DYNAMIC_SIZE> stdArray; // OK: DYNAMIC_SIZE is compile-time constant
// GRAVITY = 9.9; // Error! constexpr implies const
return 0;
}

Best Practice: Prefer constexpr for constants that can be evaluated at compile time. Use const for values initialized at runtime that should not change afterwards.

The preprocessor directive #define can also create symbolic names for values (macros).

#define MAX_ITEMS 10 // Old C-style, avoid for constants

However, this is generally discouraged in modern C++ for defining constants because:

  • It lacks type information (simple text substitution).
  • It does not respect scope (macros are global within the translation unit after definition).
  • It can lead to unexpected behavior due to macro expansion rules.
  • It isn’t easily debuggable (the debugger sees the substituted value, not the macro name).

Use const or constexpr instead for type safety and better code structure.