20 topics
/
← Back to Quick Reference
Topic 02

Data Types

Integers · Floats · Chars · auto · Conversions · Type Traits

C++17 · Advanced Reference

The C++ Type System

01

Two categories of types

C++ types divide into fundamental (built into the language) and compound (built from fundamentals — pointers, references, arrays, functions, classes). Every expression has a type known at compile time.

Fundamental

  1. 1.Integer types — int, short, long, long long, unsigned variants
  2. 2.Floating-point — float, double, long double
  3. 3.Character — char, wchar_t, char8_t, char16_t, char32_t
  4. 4.Boolean — bool (true / false)
  5. 5.Void — void (no value; used for functions and pointers)
  6. 6.std::nullptr_t — type of the literal nullptr

Key rules

  1. 1.The standard only specifies minimum sizes — use <cstdint> fixed-width types when exact size matters
  2. 2.sizeof(char) == 1 always; everything else is implementation-defined
  3. 3.Signed integer overflow is undefined behavior — unsigned overflow wraps
  4. 4.bool is an integer type — true converts to 1, false to 0
  5. 5.Floating-point arithmetic is not exact — never use == to compare doubles
  6. 6.Prefer auto for local variables; it can't be left uninitialized

Integer Types

02
// Signed integers (can hold negative values)
short           s  = 32767;          // 16-bit  ±32 767
int             i  = 2147483647;     // 32-bit  ±2.1 × 10⁹
long            l  = 2147483647L;    // 32-bit on most 64-bit systems
long long       ll = 9223372036854775807LL; // 64-bit ±9.2 × 10¹⁸

// Unsigned integers (non-negative only, double the positive range)
unsigned int    u  = 4294967295u;    // 32-bit  0 – 4.3 × 10⁹
unsigned long long ull = 18446744073709551615ULL;

// Fixed-width types (always the same size — prefer these)
#include <cstdint>
int8_t   a = 127;          int16_t  b = 32767;
int32_t  c = 2147483647;   int64_t  d = 9223372036854775807LL;
uint8_t  e = 255;          uint32_t f = 4294967295u;

// Size-semantic types
size_t    n = sizeof(int);   // result of sizeof — always unsigned
ptrdiff_t p = ptr2 - ptr1;  // difference between pointers — signed
int8_t / uint8_tExactly 8 bits. uint8_t is often used as a byte alias.
int32_t / uint32_tExactly 32 bits. Prefer over plain int when size matters.
size_tUnsigned type for object sizes and array indices. Use for loop counters over containers.
ptrdiff_tSigned type for pointer arithmetic results.

Floating-Point Types

03
float       f  = 3.14f;          // 32-bit  ~7 decimal digits
double      d  = 3.14159265358;  // 64-bit  ~15 decimal digits  ← default
long double ld = 3.14159265358L; // 80 or 128-bit (platform-dependent)

// Literal suffixes
1.0f    // float
1.0     // double (default)
1.0L    // long double

// Special values (from <cmath> / <limits>)
#include <limits>
double inf  = std::numeric_limits<double>::infinity();
double nan  = std::numeric_limits<double>::quiet_NaN();
double eps  = std::numeric_limits<double>::epsilon(); // ~2.2e-16

// Checking for special values
#include <cmath>
std::isinf(x);   std::isnan(x);   std::isfinite(x);

// Pitfall: floating-point comparison
double a = 0.1 + 0.2;
a == 0.3;              // ❌ false! (binary representation error)
std::abs(a - 0.3) < 1e-9;  // ✅ use epsilon comparison
Default to double. float saves memory but has only ~7 digits of precision — enough for graphics, not for financial or scientific work. long double is 80-bit on x86 but only 64-bit on MSVC and ARM.

Character Types

04
char     c1 = 'A';      // 8-bit, may be signed or unsigned
wchar_t  c2 = L'Ω';    // wide char — 16-bit (Windows) or 32-bit (Linux)
char8_t  c3 = u8'a';   // C++20: UTF-8 code unit
char16_t c4 = u'α';    // UTF-16 code unit
char32_t c5 = U'😀';  // UTF-32 code point (always 32-bit)

// Common escape sequences
'\n'  // newline       '\t'  // tab
'\r'  // carriage return '\\'  // backslash
'\0'  // null term.    '\''  // single quote

// char arithmetic (chars are just small integers)
char ch = 'A';
ch + 1;          // 66 — arithmetic promotes to int
(char)(ch + 1);  // 'B' — cast back to char
std::isalpha(ch); std::toupper(ch);  // <cctype>
Signedness of char is implementation-defined. If you need a small integer, use signed char or unsigned char explicitly. Use char only for characters.

auto & decltype

05
// auto — compiler deduces type from initializer
auto x    = 42;          // int
auto y    = 3.14;        // double
auto z    = 3.14f;       // float
auto s    = "hello";     // const char*
auto str  = std::string{"hello"}; // std::string

// auto strips top-level const/reference — add them back explicitly
const auto ci = 42;       // const int
auto& r = someVector;     // reference to someVector's type
const auto& cr = vec;     // const reference

// decltype — type of an expression without evaluating it
int a = 5;
decltype(a)   b = 10;    // int
decltype(a+b) c = 15;    // int (result type of a+b)

// decltype(auto) — preserves references and const (C++14)
decltype(auto) ref = getRef();  // keeps reference if getRef() returns one

// Trailing return type (useful for templates)
auto add(int a, int b) -> int { return a + b; }
autoDeduces type from initializer. Strips top-level const and references.
const autoDeduced type + const. Use for values you won't modify.
auto&Deduced type + reference. Avoids copies in range-for loops.
decltype(x)Type of expression x, without evaluating it. Preserves refs and const.
decltype(auto)Like auto but preserves reference and const — useful for forwarding return types.

Conversions & Casts

06
// Implicit promotions (safe — no data loss)
char    int  long  long long  float  double  long double

// Narrowing (unsafe — may lose data, compiler warns with -Wall)
double d = 3.99;
int    i = d;          // i = 3 (truncated, not rounded)
int    j = 300;
char   c = j;          // implementation-defined (likely truncated)

// Explicit casts
static_cast<int>(3.9)         // 3   — safe, compile-time checked
static_cast<double>(5) / 2    // 2.5 — int → double before division
reinterpret_cast<char*>(&i)   // raw memory reinterpretation
const_cast<int*>(cptr)        // remove const (use sparingly)

// Pitfall: integer overflow (undefined behavior for signed types)
int max = std::numeric_limits<int>::max();
max + 1;   // ❌ undefined behavior — signed overflow is UB
// Use unsigned if you need wrapping: uint32_t wraps predictably
Prefer static_cast over C-style casts. C-style (int)x silently tries static_cast, reinterpret_cast, and const_cast in order — you lose control over which one fires.

Literals & Suffixes

07
Prefixes & Suffixes
// Integer literal prefixes
255    // decimal
0377   // octal (leading zero)
0xFF   // hexadecimal
0b1111'1111  // binary (C++14)

// Digit separator (C++14) — readability only, no semantic meaning
1'000'000    // one million
0xFF'FF'FF   // 24-bit color
0b1010'1010  // byte with visual grouping

// Integer literal suffixes
42u    // unsigned int
42L    // long
42LL   // long long
42ULL  // unsigned long long

// String literals
"hello"         // const char[]
L"hello"        // const wchar_t[]
u8"hello"       // const char8_t[] (UTF-8)
u"hello"        // const char16_t[]
U"hello"        // const char32_t[]
R"(raw
string)" // raw string — backslashes are literal

Type Traits (C++17)

08
#include <type_traits>

// Query types at compile time
std::is_integral_v<int>         // true
std::is_floating_point_v<float> // true
std::is_same_v<int, int32_t>    // platform-dependent!
std::is_signed_v<unsigned int>  // false
std::is_const_v<const int>      // true
std::is_pointer_v<int*>         // true
std::is_reference_v<int&>       // true

// Transform types
std::remove_const_t<const int>   // int
std::remove_reference_t<int&>    // int
std::add_pointer_t<int>          // int*
std::make_unsigned_t<int>        // unsigned int
std::decay_t<const int&>         // int (mimics pass-by-value)

// Use in if constexpr (see Program Structure topic)
template<typename T>
void print(T val) {
  if constexpr (std::is_integral_v<T>)
    std::cout << "int: " << val;
  else if constexpr (std::is_floating_point_v<T>)
    std::cout << "float: " << val;
}
is_integral_v<T>True for bool, char, short, int, long, long long, and their unsigned/signed variants.
is_same_v<T, U>True only if T and U are exactly the same type — cv-qualifiers matter.
decay_t<T>Mimics pass-by-value decay: removes ref, const, converts arrays to pointers.
conditional_t<B,T,F>Type alias for T if B is true, F otherwise — compile-time ternary for types.