Fundamental Types

Table of Contents

1. Fundamental Types

Fundamental types in C++ are comprised of the type void nullptr and the Arithmetic Types listed in the below table.

Fundamental types are also called basic types, primitive types, built-in types, intrinsic types

Category Type Min Size Description
void void Functions are functions that don’t return any value. void Pointers has no set type, it can point to any data type. Must be dereferenced explicitly before usage. There are no void objects, arrays, references to void.
std::nullptr_t A distinct type (not a pointer) representing a null pointer. typedefed as nullptr in <cstddef>. Introduced in C++11
Integral
bool * A type that can hold the values true and false. When converting to/from integrals 0 means false any other numeric value means true including negative values. *Implementation defined, usually 1 byte
char 1 byte An 8-bit integral value that is used to represent ASCII character set. Range is 0…255 or -127…128, so the signedness is implementation defined. signed char and unsigned char are other distinct types that can be used where it matters. Since C++14, this type is large enough to represent UTF-8 code unit.
wchar_t 2 4 bytes
see description
An integral value that is used to represent wide characters. 32 bits in systems that support Unicode. Windows specific: 16 bits and holds UTF-16 code units.
char16_t 2 bytes An 16-bit integral value that is used to hold UTF-16 code units.
char32_t 4 bytes An 32-bit integral value that is used to hold UTF-32 code units.
int 4 bytes A 32-bit integral type that can hold integer values between -2,147,483,648…2,147,483,647 or up to 4,294,967,295 when unsigned.
Floating Point
float 4 bytes Single precision floating point number. 6 significant digits
double 8 bytes Double precision floating point number. 10 significant digits
long double 8 bytes Extended double precision floating point number. 10 significant digits. It’s typically 12 or 16 bytes (triple or quadruple precision) based on implementation.

Figure 1.1. Table of fundamental types in C++.

To check the size in bytes for your compiler, use the sizeof operator (e.g. std::cout << sizeof(int) << std::endl;)

Size Signedness
short or short int Use at least 2 bytes
216 values, [–32,768..32,767]
signed Signed range/representation (default)
long or long int Use at least 4 bytes
232 values, [~–2.1 billion..~2.1 billion]
unsigned Unsigned range/representation
long long or long long int Use at least 8 bytes
264 values, [~–9.2 quintillion..~9.2 quintillion]

Figure 1.2. Table of integer type modifiers.

All integral types are signed by default.
Floating point types don’t have unsigned versions.

1.1. Literals

  • Any value assignable to a built-in variable is a literal.

  • Literals have types. 5 is an integer literal, 5.5 is a double literal. 'a' is a char literal. "Test" is a string literal (a null terminated constant char array).

  • Some literals take prefix and suffixes specifying the type.

Suffix/Prefix Usage Description
F or f 5.5f float
L or l 500L
500.0L
L"Test"
long int
long double
wchar_t
LL or ll 500LL long long int
u8 u8"Test" UTF-8 char, applicable to strings only
u u"Test"
50u
char16_t
unsigned int
U U"Test"
50U
char32_t
unsigned int
0 050 Octal representation
0x 0x50 Hexadecimal representation

Figure 1.3. Table of literal prefixes and suffixes.

Some of the the prefixes can be mixed for a desired outcome. e.g. 500ULL is unsigned long long

  • Technically there are no negative decimal literals. The - sign is an operator that negates the value of the literal.

  • Floating point literals can be written in both scientific and decimal form.
    Floating point long double literal 3e2L = 3 * 102 = 300.0L
    Floating point float literal 3e-2F = 3 * 10-2 = 0.03F

  • The exponent is optional in floating point literals. .14 is the same as 0.14

1.2. Type Conversions

  • Built-in type conversions are implicit by default.

  • The following rules apply not only for assignment but also usage of a type where another type was expected.

  • When a bool variable is assigned an integral value, a value of 0 is converted to false and any other values are converted to true.

  • When a bool is assigned to an integral type variable, the value of the assigned variable is 1 if the bool was true and 0 if the bool was false

  • When an unsigned integral variable receives a value outside it’s range, the value of the variable “wraps around”. Consider the upper limit of an unsigned short which is 65,535. It’s 1111 1111 1111 1111 in binary form. When we add a value of 1 to exceed the upper range it’s the binary value becomes 0000 0001 0000 0000 0000 0000 which is not 2 bytes anymore but 3. Since it can’t fit the range of our short it’s “wrapped around” by VALUE modulo MAX_VALUE or 65536 % 65536 which is 1.

  • When a signed integral variable receives a value outside it’s range, the result is undefined. The program may crash, produce irrelevant values or the value may get “wrapped around” just like unsigned integrals. For example short int or short has a range of [–32,768..32,767]. If the value 32,768 (short int range + 1) is assigned, the variable will hold the value –32,768. The value we’ve assigned, 32,768 is an integer literal so it would work the same in the following code snippet

int a = 32768;
short int b = a;

std::cout << b << std::endl; // Prints –32,768

But the important bit is the results are undefined thus one shouldn’t write code like this.

  • When a floating point type variable is assigned to an integral type variable, the value is truncated. The assigned variable receives the decimal part. (int x = 5.89; // x becomes 5). If the assigned floating point value is outside the range of the integral variable, the closest value it’s range supports is assigned instead.
short x = 12345678.55;
std::cout << x << std::endl; // Prints 32676

short y = -9876543.25;
std::cout << y << std::endl; // Prints -32768

One expects the value x to receive only the decimal part of the assigned double value, 12345678 and wrap the type range overflow as if it’s assigned an integral value outside it’s range.

What happens is, the fractional part is truncated and the variable x receives the highest value it’s range supports which is 32676.

  • When an integral value is assigned to a floating point variable, the fractional part is set as 0 (double val = 50; // val becomes 50.0). If the assigned integer has more bits than the floating point variable can hold, precision may be lost.

Don’t mix unsigned and signed types in expressions.
Don’t assign values or variables to variables of types that can’t support their size.