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:

  1. Determine a common real type for the operands and result.
  2. Convert each operand, without change of type domain, to a type with real type matching the common real type.
  3. 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:

  1. long double
  2. double
  3. float
  4. If both operands have the same signedness, the higher ranked type.
  5. If the unsigned operand has rank than that of the other, the unsigned type.
  6. If the signed operand type can accommodate that of the other, the signed type.
  7. 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);
Powered by Forestry.md