Skip to content
C++
Language
since C++98
Basic

Escape Sequences

Backslash-prefixed sequences in string and character literals that represent otherwise unrepresentable or syntactically ambiguous characters.

Escape Sequencessince C++98

An escape sequence is a backslash followed by one or more characters within a string or character literal that the compiler translates β€” at compile time β€” into a single character value that would otherwise be syntactically reserved, non-printable, or platform-specific.

Overview

Escape sequences are resolved during translation phase 5, before any runtime execution. They appear in character literals ('\n'), narrow string literals ("\n"), and their prefixed variants (u8"\n", u"\n", U"\n", L"\n"). There are three categories: simple sequences with fixed semantics, numeric sequences specifying code points directly, and universal character names for Unicode portability.

Simple Escape Sequences

SequenceMeaningASCII
\\backslash0x5C
\'single quote0x27
\"double quote0x22
\?question mark0x3F
\aaudible bell0x07
\bbackspace0x08
\fform feed0x0C
\nline feed (newline)0x0A
\rcarriage return0x0D
\thorizontal tab0x09
\vvertical tab0x0B

\? exists to prevent trigraph interpretation (e.g., ??= would otherwise be tokenised as #). Trigraphs were deprecated in C++14 and removed in C++17, so \? has no relevance in modern code targeting C++17 or later.

The null character (\0) is technically the octal escape sequence with value zero, not a simple escape sequence, but it is universally treated as a named constant.

Numeric Escape Sequences

Octal

cpp
char bell  = '\a';    // simple escape
char bell2 = '\007';  // same value via octal β€” 1 to 3 digits, each 0–7
char A     = '\101';  // octal 101 = decimal 65 = 'A'

Exactly 1–3 octal digits are consumed. The digit 8 and 9 are not octal digits; '\8' and '\9' are ill-formed.

cpp
char bad = '\9';   // ill-formed
char ok  = '\07';  // BEL, same as '\a'

Hexadecimal

cpp
char A    = '\x41';   // 'A'
char nul  = '\x00';   // null character
char bel  = '\x07';   // audible bell

Unlike octal, the hex form is greedy: it consumes all subsequent hexadecimal characters. When the character immediately after the escape is a hex digit, split the literal explicitly:

cpp
const char* wrong = "\xabcdef";    // one character with value 0xABCDEF β€” likely UB or truncation
const char* ok    = "\xab" "cdef"; // 0xAB then the string "cdef"
const char* also  = "\x0a" "Blue"; // '\n' then "Blue" β€” 'B' is hex so split is mandatory

C++23: Delimited Numeric Escapes

C++23 (P2290R3) introduces brace-delimited forms that solve the greedy problem and reject out-of-range values at compile time:

cpp
// C++23
char c = '\o{101}';         // octal β€” equivalent to '\101'
char d = '\x{41}';          // hex   β€” equivalent to '\x41'
const char* s = "\x{0a}face"; // unambiguous newline then "face"

char16_t bad = u'\x{FFFFFF}'; // ill-formed: value exceeds char16_t range

Universal Character Names

Universal character names (UCNs) specify Unicode code points in any string or character literal. The encoding used depends on the literal prefix.

cpp
// Available since C++98
char32_t euro    = U'\u20AC';          // U+20AC EURO SIGN β€” 4 hex digits, BMP only
char32_t snowman = U'\U00002603';      // U+2603 SNOWMAN β€” 8 hex digits, full Unicode range
const char* utf8_euro = u8"\u20AC";    // encodes U+20AC as UTF-8: 0xE2 0x82 0xAC

wchar_t wide_euro = L'\u20AC';         // platform-dependent encoding

\uNNNN requires exactly 4 hex digits. \UNNNNNNNN requires exactly 8. Both work in char, char8_t, char16_t, char32_t, and wchar_t contexts; the compiler encodes the code point appropriately for the target type.

C++23: Named Character Escapes

C++23 (P2071R2) introduces \N{name} using the official Unicode character database name:

cpp
// C++23
char32_t snowman  = U'\N{SNOWMAN}';              // U+2603
char32_t smiley   = U'\N{GRINNING FACE}';        // U+1F600
const char* euro  = u8"\N{EURO SIGN}";           // UTF-8 encoded €

Names must exactly match the Unicode character database. This is strictly a compile-time feature. Prefer it over raw code points in any context where readability matters.

C++23: Delimited Universal Character Escape

cpp
// C++23
char32_t emoji = U'\u{1F600}';   // avoids the 8-digit \U form for code points above U+FFFF

Raw String Literals (C++11)

When expressions contain many escape sequences, raw string literals eliminate them entirely:

cpp
// C++11
const char* path_esc = "C:\\Users\\alice\\Documents\\notes.txt";
const char* path_raw = R"(C:\Users\alice\Documents\notes.txt)";

const char* json_esc = "{\"key\": \"value\", \"n\": 42}";
const char* json_raw = R"({"key": "value", "n": 42})";

const char* re_esc = "(\\w+)\\s+(\\d{1,3}\\.\\d{1,3})";
const char* re_raw = R"((\w+)\s+(\d{1,3}\.\d{1,3}))";

The syntax is R"delimiter(content)delimiter" where delimiter is any string of up to 16 characters (excluding space, \, (, )). Use a custom delimiter when the content contains )":

cpp
const char* tricky = R"delim(ends with )" here)delim"; // C++11

Best Practices

Prefer '\n' over std::endl in output loops. std::endl flushes the stream buffer; '\n' does not. Flushing on every line is expensive when writing large amounts of output.

cpp
for (const auto& line : lines) {
    std::cout << line << '\n';    // fast
    // std::cout << line << std::endl; // flushes each iteration β€” avoid
}

Use raw string literals for paths and regular expressions. Double-escaping is error-prone and obscures intent.

Split hex escapes explicitly when the next character is alphanumeric:

cpp
const char* ambiguous = "\x0dA";        // is this 0x0DA or CR + 'A'? β€” formally CR + 'A', but misleading
const char* clear     = "\x0d" "A";     // unambiguous: CR then 'A'

Common Pitfalls

Windows file paths. The most common production mistake involving escape sequences:

cpp
const char* broken = "C:\new_folder\test.txt";  // \n and \t are escapes β€” BUG
const char* fixed  = "C:\\new_folder\\test.txt";
const char* clean  = R"(C:\new_folder\test.txt)"; // C++11 β€” preferred

Null bytes in std::string. std::string can contain embedded null bytes, but the single-argument string literal constructor stops at the first null from strlen's perspective:

cpp
std::string s = "abc\0def";      // .size() == 3 β€” "def" is silently dropped
std::string t("abc\0def", 7);    // .size() == 7 β€” explicit length constructor
std::string u = "abc"s;          // using namespace std::string_literals (C++14)

Octal range errors. Digits 8 and 9 are not octal; combining them with \ is ill-formed:

cpp
char c = '\8';   // ill-formed
char d = '\08';  // ill-formed β€” 0 is valid octal but 8 is not
char e = '\07';  // fine β€” BEL character

Greedy hex in concatenated literals. Adjacent string literals are each processed independently, so cross-literal hex ambiguity does not occur β€” but ambiguity within a single literal's fragment still does:

cpp
const char* ok   = "\x61" "b";  // 'a' + 'b' β€” unambiguous
const char* trap = "\x61b";     // 0x61B, not 'a' + 'b' β€” single literal, greedy

\r\n vs \n in binary I/O. Text-mode streams on Windows translate \n to \r\n on write. When working with network protocols, binary file formats, or cross-platform data, open streams in binary mode and write \r\n explicitly rather than relying on platform translation.

cpp
// For CRLF-terminated HTTP headers:
std::string header = "HTTP/1.1 200 OK\r\n"
                     "Content-Type: text/plain\r\n"
                     "\r\n";

Universal Character Names in Depth

Universal character names (UCNs) have been part of C++ since C++98 and are the canonical mechanism for embedding arbitrary Unicode code points in source code in a portable, encoding-independent way.

Two Forms

\uNNNN uses exactly 4 hex digits and can express any code point in the Basic Multilingual Plane (U+0000 through U+FFFF).

\UNNNNNNNN uses exactly 8 hex digits and can express any code point in the full Unicode range up to U+10FFFF.

cpp
// \uNNNN β€” BMP only (U+0000–U+FFFF)
const char32_t degree  = U'Β°';   // U+00B0 DEGREE SIGN Β°
const char32_t euro    = U'€';   // U+20AC EURO SIGN €
const char32_t chess   = U'β™ž';   // U+265E BLACK CHESS KNIGHT β™ž

// \UNNNNNNNN β€” full Unicode range including supplementary planes
const char32_t snowman = U'\U00002603'; // U+2603 SNOWMAN β˜ƒ
const char32_t emoji   = U'\U0001F600'; // U+1F600 GRINNING FACE πŸ˜€
const char32_t musical = U'\U0001D11E'; // U+1D11E MUSICAL SYMBOL G CLEF π„ž

The encoding produced in the resulting literal depends on the prefix. In a u8 literal the compiler encodes the code point as UTF-8; in a u literal as UTF-16; in a U literal as UTF-32; in an L literal as the platform's wide character encoding; in an unadorned char literal as the execution character set (usually UTF-8 on modern targets).

cpp
// Same code point, four encodings
const char*     utf8 = "€";    // 3 bytes: 0xE2 0x82 0xAC
const char8_t*  u8s  = u8"€"; // same bytes, type is const char8_t*
const char16_t* u16  = u"€";  // 1 char16_t: 0x20AC
const char32_t* u32  = U"€";  // 1 char32_t: 0x000020AC
const wchar_t*  wide = L"€";  // platform-dependent

UCNs in Identifiers

UCNs are permitted in identifiers, which allows non-ASCII names written in portable source form:

cpp
int Ξ± = 1;   // variable named Ξ± (U+03B1 GREEK SMALL LETTER ALPHA)
int cafΓ© = 2; // cafΓ©

However, the standard forbids using a UCN in an identifier if the code point it denotes falls in the range U+0000–U+001F (C0 control characters), U+007F (DELETE), or U+0080–U+009F (C1 control characters). These ranges are unconditionally excluded from being representable via UCN in identifier positions regardless of compiler extension.

Code points whose UCN encodes a character with XID_Start or XID_Continue property (per Unicode) are valid. Compilers vary in exactly which supplementary plane characters they accept in identifiers, but all mainstream compilers (GCC 10+, Clang 9+, MSVC 19.29+) accept common scripts such as Greek, Arabic, CJK Unified Ideographs, and Cyrillic.

Avoiding Surrogate Pairs

Code points in the range U+D800–U+DFFF are UTF-16 surrogate halves. They are not valid Unicode scalar values and must not appear as UCNs:

cpp
char32_t bad = U'\uD800';  // ill-formed β€” surrogate half is not a scalar value

Raw String Literals

Raw string literals, introduced in C++11, suppress all escape processing inside the string body. The compiler treats every byte between the delimiters as a literal character, including backslashes and newlines.

Syntax

R"delimiter(content)delimiter"

delimiter is an optional sequence of up to 16 characters from the set [A-Za-z0-9_{}#<>%:;.?*+/^&|~!=,\-] β€” notably excluding space, (, ), and \. The opening sequence R"delimiter( is terminated only by the matching closing sequence )delimiter".

cpp
// C++11
const char* path = R"(C:\Users\alice\Documents\notes.txt)";
const char* json = R"({"key": "value", "flag": true})";
const char* re   = R"((\d{1,3}\.){3}\d{1,3})";  // IP address regex

Multiline Raw Strings

Because newlines are not escaped, a raw string can span multiple lines exactly as written in the source file. The literal value includes the newlines:

cpp
const char* html = R"(<!DOCTYPE html>
<html>
  <body>
    <p>Hello, world.</p>
  </body>
</html>
)";
// html[0] == '<', and the string ends with '\n' before the closing )".

This is especially useful for embedding SQL, GLSL shaders, JSON schemas, or any other block text where line structure is meaningful.

Custom Delimiters for Content Containing )

If the string content must contain the sequence )", choose any delimiter that does not appear in the content as )delimiter":

cpp
const char* html_attr = R"html(onclick="doSomething()")html";
//                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
//                              content contains )" β€” safe because delimiter is "html"

const char* nested = R"---(text with )" and )--" inside)---";

The choice of delimiter name is arbitrary. Common conventions are short identifiers like raw, lit, str, or project-specific tags.

Unicode-Prefixed Raw String Literals

All encoding prefixes can be combined with R:

cpp
// C++11
const char8_t*  u8r  = u8R"(UTF-8 raw: no \n processing here)";
const char16_t* u16r = uR"(UTF-16 raw literal)";
const char32_t* u32r = UR"(UTF-32 raw literal)";
const wchar_t*  wr   = LR"(wide raw literal)";

The prefix controls the encoding of the resulting string, not how the source bytes are scanned. Escape sequences are still suppressed; only encoding of the literal bytes into the target type differs.

Raw Strings and constexpr

Raw string literals are fully usable in constexpr and consteval contexts:

cpp
constexpr const char* pattern = R"(\b\w+\b)";   // C++11 constexpr raw string

C++23 New Escape Sequences

C++23 standardised several additions that were either extensions or entirely new mechanisms. All require a conforming C++23 compiler; GCC 13+, Clang 16+, and MSVC 19.36+ have varying degrees of support.

\N{name} β€” Named Character Escapes (P2071R2)

The \N{name} form lets you write a character by its official Unicode character database name. The name is looked up at compile time; an unrecognised name is a hard error.

cpp
// C++23
char32_t snowman   = U'\N{SNOWMAN}';                  // U+2603
char32_t em_dash   = U'\N{EM DASH}';                  // U+2014
char32_t copyright = U'\N{COPYRIGHT SIGN}';            // U+00A9
char32_t grinning  = U'\N{GRINNING FACE}';             // U+1F600

const char* greeting = u8"\N{LATIN SMALL LETTER A}\N{LATIN SMALL LETTER B}";
// identical to u8"ab", but names make intent explicit in generated-code contexts

Names must match Unicode exactly, including spaces and capitalisation. Common names to know:

NameCode PointGlyph
LATIN SMALL LETTER AU+0061a
DIGIT ZEROU+00300
SNOWMANU+2603β˜ƒ
EM DASHU+2014β€”
EURO SIGNU+20AC€
GRINNING FACEU+1F600πŸ˜€
ZERO WIDTH NO-BREAK SPACEU+FEFF(BOM)

\N{name} is not permitted in raw string literals β€” nothing is: raw strings suppress all escape processing.

Compiler support as of mid-2026: GCC 13 (full), Clang 17 (full), MSVC 19.36 (partial). Check your project's minimum supported compiler before relying on this in production headers.

\e β€” The Escape Character (P2290R3)

GCC and Clang have long supported \e as a non-standard extension for the ESC character (U+001B, decimal 27, octal \033, hex \x1B). C++23 standardises it:

cpp
// C++23 β€” previously a GCC/Clang extension
const char* reset = "\e[0m";    // ANSI SGR reset
const char* bold  = "\e[1m";    // ANSI SGR bold
const char* red   = "\e[31m";   // ANSI SGR red foreground

// Equivalent pre-C++23 forms:
const char* reset_old  = "\x1B[0m";   // hex β€” always worked
const char* reset_oct  = "\033[0m";   // octal β€” always worked
const char* reset_ucn  = ""; // UCN β€” always worked

\e is strictly syntactic sugar for \x1B; there is no runtime difference. Its primary value is readability: the intent β€” "this is an ANSI terminal escape sequence" β€” is immediately visible without mental arithmetic.

Delimited Escape Sequences (P2290R3)

C++23 also formalises brace-delimited variants of octal, hex, and universal character name escapes. These are covered in the Numeric Escape Sequences section above, but the rationale deserves emphasis here: they eliminate two long-standing footguns.

The greedy hex problem: "\x0dBlue" is not CR + "Blue" β€” it is a single character with code point 0x0DB because B, l, u, e are not hex but B is. Wait: l, u, e are not hex; B is. So \x0dB (0xDB) followed by "lue". C++23 "\x{0d}Blue" is unambiguous.

The octal-digit-count problem: '\012' is newline but '\0129' is '\012' followed by '9' as a multi-character literal (or ill-formed depending on context). '\o{12}' accepts any number of octal digits inside the braces.

cpp
// C++23 delimited forms
char lf  = '\o{12}';         // octal newline, unambiguous
char esc = '\x{1B}';         // ESC, no greediness concern
char u   = '\u{263}';        // U+0263 LATIN SMALL LETTER GAMMA (variable digit count)
char32_t face = U'\u{1F600}'; // supplementary plane without 8-digit \U form

String Literal Concatenation and Encoding

Adjacent string literal tokens are concatenated by the compiler during translation phase 6, after escape sequence resolution. This affects how encodings interact.

Basic Concatenation

cpp
// These are all identical
const char* a = "hello " "world";
const char* b = "hello " R"(world)";    // raw + ordinary β€” same result
const char* c = "hel" "lo " "world";    // three tokens

Mixing Prefixes

The C++11 rules for concatenating literals with different prefixes:

  • If one operand is an unsuffixed (ordinary) string literal and the other is prefixed, the result has the prefixed encoding.
  • If both are prefixed, they must have the same prefix (otherwise the program is ill-formed).
  • Mixing u8 with u or U is ill-formed.
cpp
// Legal: one operand is unsuffixed
const char16_t* p = u"hello " "world";     // result is u"hello world"
const char32_t* q = U"prefix: " "value";   // result is U"prefix: value"
const wchar_t*  r = L"path: " R"(\foo)";   // L"path: " + raw β†’ L"path: \\foo" equivalent

// Ill-formed: both prefixed but different
// const char* bad = u8"a" u"b";   // error: u8 + u
// const char* bad = u"a"  U"b";   // error: u + U

char8_t Concatenation (C++20)

C++20 changed u8 string literals to produce const char8_t* rather than const char*. This affects concatenation: you cannot concatenate a u8 literal with an unsuffixed char literal and assign to const char*.

cpp
// C++20
const char8_t* s = u8"hello " u8"world";  // fine
// const char*  t = u8"hello " "world";   // may warn or error: char8_t* vs char*

If you need to mix u8 content with byte strings in C++20, cast explicitly or use std::bit_cast / reinterpret_cast with care.

Concatenation Across Translation Units

String literal concatenation is a compile-time lexical operation β€” it cannot span translation units. Literals in separate .cpp files are never automatically concatenated; that is a link-time operation using arrays or extern variables.

Using String Literal Suffix s (C++14)

The std::string_literals::operator""s suffix produces a std::string and is aware of embedded null bytes:

cpp
using namespace std::string_literals;

std::string a = "hello\0world"s;   // length 11, embedded null preserved
std::string b = u8"cafΓ©"s;         // std::string from u8 literal (C++14/17)
// std::u8string c = u8"cafΓ©"s;    // std::u8string from u8 literal (C++20)

The s suffix does not affect escape-sequence resolution; it merely controls the type of the resulting object.


See Also

  • String literal prefixes: u8, u, U, L and the s suffix (C++14)
  • char8_t (C++20), char16_t and char32_t (C++11) β€” Unicode character types
  • Raw string literals (C++11): R"(...)"
  • std::string_literals and operator""s (C++14)