Primitive Types

Primitive types (or fundamental types) are built-in types that are part of the core language, and almost always available to you.

These types will work on any platform, but their features (e.g. size and memory layout) may differ depending on the infrastructure.

"F.S."

Each type has their own printf() format specifier, which I will abbreviate "F.S."

You will normally use this format specifier to print a variable/expression of that type using printf().

Integer types

These store integers (whole numbers).

Types of integer types

  • There are four sizes: short (2 bytes), int (4 bytes), long (4 or 8 bytes), and long long (8 bytes).

    • Thanks to the specification, long can be 4 bytes or 8 bytes. (It's 8 bytes on 64-bit Linux or Mac; 4 bytes elsewhere.)
  • Each integer type can be signed (e.g. int) or unsigned (e.g. unsigned int)

    • If signed, it can store negative, zero, and positive values
    • If unsiged, it can store zero and positive values only
TypeSizeMin valueMax valueF.S.
short2 bytes32,76832,767%hd
unsigned short2 bytes065,535%hu
int4 bytes (2.14)×109 2.14×109%d
unsigned int4 bytes0 4.29×109%u
long4 or 8 bytes%ld
unsigned long4 or 8 bytes%lu
long long8 bytes (9.22)×1018 9.22×1018%lld
unsigned long long8 bytes0 18.44×1018%llu

Integer literals

When we say literal, it means hardcoded values in your program.

  • To write in decimal just write the number normally, e.g. 1234 or -1234
  • To write in ocatal (base 8) prefix the number with 0, e.g. 067 or -067
  • To write in binary (base 2) prefix the number with 0b, e.g. 0b0011 or -0b0011
  • To write in hexadecimal (base 16) prefix the number with 0x, e.g. 0x9ab or -0x9ab

When you write an integer literal, the compiler will assume its type as either int, long, or long long (it will pick the smallest one that fits). If you want more control, you can narrow down its type by suffixing it with any of these (they are case insensitive).

  • l or L to make it a SIGNED long (or long long)
  • ll or LL to make it a SIGNED long long
  • u or U to make it an unsigned int (or unsigned long or unsigned long long)
  • ul or UL to make it an unsigned long (or unsigned long long)
  • ull or ULL to make it an unsigned long long

Formatting integers

  • %o format-print an integer type in its octal representation
  • %x format-print an integer type in its hexademical representation
int number = 123;
printf("%d in HEX is %x!\n", number, number);
//=> 123 in HEX is 7b!

Floating-point types

These store approximations of real numbers. (Because the space to store the numbers are limited, there could be precision errors when representing real numbers.)

Types of floating-point types

long double is usually the same as double (just use double).

TypeSizeMin valueMax valueF.S.
float4 bytes (3.40)×1038 3.40×1038%f
double8 bytes (1.79)×10308 1.79×10308%lf
long double8 bytes (1.79)×10308 1.79×10308%Lf

Floating-point literals

There are two ways to write a floating-point literal:

  • Write it normally e.g. 123.45, -123.45, 456 (integer literals are valid, too)
  • Write it in scientific notation e.g. 1.9e2 (1.9×102) or -2.1e-2 (2.1×10-2)

When you write a floating-point literal, the compiler will assume its type as double by default. If you want more control, you can specify its type by suffixing it with any of these (they are case insensitive).

  • f or F to make it a float, e.g. 2.7f
  • l or L to make it a long double, e.g. 4.8L

Formatting floating-point numbers

Lospinoso suggests using %g to print floating-point numbers.
  • %f, %lf, or %Lf print normally (with trailing zeroes), e.g. 54321.000000 or 9.750000
  • %e format-print the number in scientific notation, e.g. 6.02214e+23 or 9.750000e+00
  • %g choose the shortest representation between %f (or %lf) and %e, removing the trailing zeroes, e.g. 6.02214e+23 or 9.75

You can further format your number with additional flags/arguments in your printf format specifier. For example, printing 1.2555 with %+08.2lf will give you +0001.26:

  • the + prefix flag means always print the sign, i.e. + or -
  • the 0 prefix flag means left-pad the number with zeroes
  • the 8 width argument means the formatted number will take at least 8 characters
  • the .2 precision argument means always display 2 digits after the decimal point (the default is 6)

Character types

These store a single human language character, e.g. "A".

Types of character types

  • "Narrow characters":
    • char May be signed or unsigned (depending on the compiler). It can store an ASCII character or a 1-byte integer.
  • "Wide characters" (because they can store more than 1 byte):
    • char16_t It can store a 2-byte character set, e.g. an UTF-16 character.
    • char32_t It can store a 4-byte character set, e.g. an UTF-32 character.
    • wchar_t May be 2 bytes or 4 bytes (depending on the compiler). It can store, for example, Unicode characters (2 bytes).
TypeSizeMin valueMax valueF.S.
char1 byte%c
signed char1 byte-128127%c
unsigned char1 byte0255%c
char16_t2 bytes065,535*
char32_t4 bytes0 4.29×109*
wchar_t2 or 4 bytes%lc

(*) char16_t and char32_t don't exactly have a format specifier (you will need to convert them first if you want to print them). Alternatively, you can use %#x to print the character using their hexadecimal representation, e.g. 0x6c34 for (The # flag adds the 0x prefix).

Character literals

Quote them using single quotes ('), e.g. 'x'.

If the type is not char, you must also give the following prefix:

  • u to make it char16_t, e.g. u'x'
  • U to make it char32_t, e.g. U'x'
  • L to make it wchar_t, e.g. L'x'

Character escapes

To enter special characters:

  • \r to enter the carriage return character
  • \n to enter the newline character
  • \t to enter the tab character
  • \0 to enter the null character
  • \' to enter the ' character
  • \" to enter the " character
  • \\ to enter the \ character
  • \0 to enter the null character

To enter Unicode characters:

  • \uXXXX (4-digit Unicode code point) e.g. \u0041 to enter A
  • \UXXXXXXXX (8-digit Unicode code point) e.g. \U0001F37A to enter 🍺

Boolean types

These store true or false.

Types of boolean types

There is only a single boolean type: bool.

(Note that even though technically it only requires a single bit to store a true/false value, bool requires 1 byte.)

TypeSizeMin valueMax valueF.S.
bool1 byte01**

(**) There are no format specifier for the bool type. Instead, you can use %d to print it as 0 oir 1.

Boolean literals

Just write true or false.

Additionaly, the following shows the equivalence between boolean and integer types:

  • true is equivalent to 1
  • false is equivalent to 0
  • Any non-zero integer value is equivalent to true
  • Zero is equivalent to false

Void types

The void type stores no value.

In fact, you cannot define a variable with a void type. You use void in "special situations," for example, to denote the return type of a function that doesn't return anything.

void greet() {
  printf("Hello!\n");
}

Other notable types

std::byte

Defined in <cstddef> (#include <cstddef>), this type is usually used to handle raw memory (collection of bits without a type).

size_t

Defined in <cstddef> (#include <cstddef>), this type represents the size of an object in bytes. (Its actual type depends on the compiler, but you can think of it as an unsigned integer, usually unsigned long long.)

When you use the typeof operator, e.g. sizeof(double), the return type would be size_t. Its format specifier is %zu.

size_t size_double = sizeof(double);
printf("%zu\n", size_double);
//=> 8 (because double type requires 8 bytes)

References