Simple Types
Overview
The bottom of the type hierarchy consists of simple types. This comprises the primitive types that all other types are either based off of or derived from.
| Signed | Unsigned | Real | Complex | 32-bit | 64-bit |
|---|---|---|---|---|---|
| - | bool | - | - | 1 | 1 |
signed char | unsigned char | - | - | 1 | 1 |
short | unsigned short | - | - | 2 | 2 |
int | unsigned | - | - | 4 | 4 |
long | unsigned long | - | - | 4 | 8 |
long long | unsigned long long | - | - | 8 | 8 |
| - | - | float | - | 4 | 4 |
| - | - | double | - | 8 | 8 |
| - | - | long double | - | - | - |
| - | - | - | float complex | 4 | 4 |
| - | - | - | double complex | 8 | 8 |
| - | - | - | long double complex | - | - |
Integer Types
Narrow types cannot be used directly in arithmetic. Instead they are first promoted to a wider type. On almost every system, this promotion will be to a signed int of the same value, regardless of the signedness of the narrow type itself.
Additionally, the following suffixes can be used to constrain types:
| Suffix | Constraint |
|---|---|
u | unsigned |
l | long or long long |
ll | long long |
wb | _BitInt |
Characters
The three types char, signed char, and unsigned char are collectively called the character types. The implementation defines char to have the same range, representation, and behavior as either signed char or unsigned char, but is considered incompatible with both. That is, it is a distinct type in the eyes of the type system.
Unsigned Integers
Unsigned integers employ unsigned encoding. Overflow is well-defined, wrapping around as expected.
| Name | Narrow | Rank | Minimum Width |
|---|---|---|---|
bool | Yes | 0 | 1 |
char (maybe) | Yes | 1 | - |
unsigned char | Yes | 1 | 8 |
unsighed short | Yes | 2 | 16 |
unsigned int | No | 3 | 16 |
unsigned long | No | 4 | 32 |
unsigned long long | No | 5 | 64 |
Signed Integers
Prior to C23, signed integers had no required encoding. Since C23, they must use two's-complement. Overflow is undefined.
| Name | Narrow | Rank | Minimum Width |
|---|---|---|---|
char (maybe) | Yes | 1 | - |
signed char | Yes | 1 | 8 |
signed short | Yes | 2 | 16 |
signed int | No | 3 | 16 |
signed long | No | 4 | 32 |
signed long long | No | 5 | 64 |
float | - | - | - |
double | - | - | - |
long double | - | - | - |
Enumerations
Enumerations, declared with the enum keyword, are mappings between identifiers and integer values. Members of an enum are called enumeration constants.
All enumerations are associated with an underlying type. In C17, the underlying type of an enumeration is either a char, signed integer type, or unsigned integer type, compatible with the enumeration constants. The choice is implementation-defined. Regardless, ICEs used to define the constants are required to fit within a signed int representation.
Since C23, the underlying type can be declared explicitly by specifying : <T> before the body. If no underlying type is declared, the underlying type defaults to the common compatible type of the constants. This is a char, signed integer type, or unsigned integer type.
Floating-Point Types
The floating-point types are divided into the real and complex classes. The floating-point environment, consisting of status flags and control modes, is accessible via the <fenv.h> header.
Additionally, the following suffixes can be used to constrain types:
| Suffix | Constraint |
|---|---|
f | float |
l | long double |
df | _Decimal32 |
dd | _Decimal64 |
dl | _Decimal128 |
Binary
The binary floating-point types are float, double, and long double. Prior to C23, the choice of floating-point representation was unspecified. Since C23, these types must obey the IEEE-754 binary floating-point standard.
Decimal
Included in C23 are the decimal floating-point types _Decimal32, _Decimal64, and _Decimal128. These types must obey the IEEE-754 decimal floating-point standard. Whether decimal floating-point types and operations are supported is implementation-specific.
Complex
The complex types are float _Complex, double _Complex, and long double _Complex. Whether complex types and operations are supported is implementation-specific.
Usual Arithmetic Conversions
As a general rule, the result of an operation has the type of the operand with wider range. The usual arithmetic conversions behave according to the following pattern:
- Determine a common real type for the operands and result.
- Convert each operand, without change of type domain, to a type with real type matching the common real type.
- Unless explicitly stated otherwise, the common real type is the corresponding real type of the result, whose type domain is that of the operands if they are the same and complex otherwise.
Common real types are prioritized in the following order:
long doubledoublefloat- If both operands have the same signedness, the higher ranked type.
- If the unsigned operand has rank
than that of the other, the unsigned type. - If the signed operand type can accommodate that of the other, the signed type.
- The unsigned integer type corresponding to that of the signed integer type.
Checked Arithmetic
In C23, the <stdckdint.h> header was introduced. It defines several macros for performing checked integer arithmetic. Operations behave as if operands were signed integer types with infinite range. The result is then converted into the desired type.
bool ckd_add(type1 *result, type2 a, type3 b);
bool ckd_sub(type1 *result, type2 a, type3 b);
bool ckd_mul(type1 *result, type2 a, type3 b);