Fundamental types in C++ are comprised of the type
nullptr and the Arithmetic Types listed in the below table.
Fundamental types are also called basic types, primitive types, built-in types, intrinsic types
|std::nullptr_t||A distinct type (not a pointer) representing a null pointer.
|bool||*||A type that can hold the values
|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.
|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.|
|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;)
||Use at least 2 bytes
216 values, [–32,768..32,767]
||Signed range/representation (default)|
||Use at least 4 bytes
232 values, [~–2.1 billion..~2.1 billion]
||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
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.
|F or f||
|L or l||
|LL or ll||
||long long int|
||UTF-8 char, applicable to strings only|
Figure 1.3. Table of literal prefixes and suffixes.
Some of the the prefixes can be mixed for a desired outcome. e.g.
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 =
Floating point float literal
3e-2F = 3 * 10-2 =
The exponent is optional in floating point literals.
.14 is the same as
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.
bool variable is assigned an integral value, a value of 0 is converted to
false and any other values are converted to
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
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.
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.
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.