Every object and expression in C has a type, which sets the size of its storage and the meaning of its bits. The language fixes only minimum ranges, so the exact sizes of most types depend on the implementation (C17 6.2.5, 5.2.4.2).
Kinds of type
| Kind | Types |
|---|---|
| Integer | char, the signed and unsigned integer types, enumerated types, bool and _BitInt |
| Floating | float, double, long double, their complex forms, and the C23 decimal types |
| Arithmetic | Integer types and floating types together |
| Scalar | Arithmetic types and pointer types, and nullptr_t in C23 |
| Aggregate | Arrays and structures |
| Derived | Arrays, structures, unions, functions, pointers and atomic types |
| Incomplete | void, an array of unknown size, or a structure or union whose contents are not yet known |
An object type describes storage. A function type describes a function and is not an
object type. An incomplete type cannot be used to define an object or as an operand of
sizeof, but a pointer to one is allowed.
Integer types
The standard requires at least these ranges. They are the values of the macros in
<limits.h> that
an implementation must meet or exceed (C17 5.2.4.2.1).
| Type | Minimum range (C17) | Minimum width |
|---|---|---|
bool | 0 to 1 | |
char | Same as signed char or unsigned char | 8 bits |
signed char | −127 to 127 | 8 bits |
unsigned char | 0 to 255 | 8 bits |
short | −32767 to 32767 | 16 bits |
unsigned short | 0 to 65535 | 16 bits |
int | −32767 to 32767 | 16 bits |
unsigned int | 0 to 65535 | 16 bits |
long | −2147483647 to 2147483647 | 32 bits |
unsigned long | 0 to 4294967295 | 32 bits |
long long (C99) | −9223372036854775807 to 9223372036854775807 | 64 bits |
unsigned long long (C99) | 0 to 18446744073709551615 | 64 bits |
C23 requires two’s complement, so the minimum of each signed type drops by one:
signed char reaches −128, and int at 16 bits reaches −32768.
Earlier standards also allowed sign-and-magnitude and ones’ complement, which is why the
older limits are symmetrical.
sizeof(char)is 1 by definition, whatever the width. The width isCHAR_BIT, which is at least 8 and is 8 on current systems.char,signed charandunsigned charare three distinct types. Whether plaincharis signed is implementation-defined.- The other integer types may also be written in a longer form:
shortisshort intorsigned short int, andintalone is the same assignedorsigned int. - The ranges nest: each signed type’s range is contained in that of the next larger type,
in the order
signed char,short,int,long,long long(C17 6.2.5).
Data models
The sizes on real platforms follow a few common patterns. These are conventions of each platform’s ABI, not rules of C.
| Model | int | long | Pointer | Used by |
|---|---|---|---|---|
| ILP32 | 32 bits | 32 bits | 32 bits | 32-bit Linux, macOS and Windows |
| LP64 | 32 bits | 64 bits | 64 bits | 64-bit Linux and macOS |
| LLP64 | 32 bits | 32 bits | 64 bits | 64-bit Windows |
Fixed-width integers
<stdint.h> (C99) names integer types by width:
| Type | Meaning | Required |
|---|---|---|
intN_t, uintN_t | Exactly N bits, with no padding | Only if the implementation has such a type |
int_leastN_t, uint_leastN_t | The smallest type of at least N bits | For 8, 16, 32 and 64 |
int_fastN_t, uint_fastN_t | Usually the fastest type of at least N bits | For 8, 16, 32 and 64 |
intptr_t, uintptr_t | Can hold a void * converted to it and back | No |
intmax_t, uintmax_t | The largest integer type | Yes |
Boolean type
_Bool (C99), spelled bool as a keyword in C23, holds 0 or 1.
When any scalar value is converted to it, the result is 0 if the value compares equal to
0, and 1 otherwise (C17 6.3.1.2). So (bool)0.5 is 1. Before C23, including
<stdbool.h> gives the names bool, true and
false.
Bit-precise integers (C23)
_BitInt(N) and unsigned _BitInt(N) are integers of exactly N
bits, up to the implementation’s BITINT_MAXWIDTH. They do not undergo the
integer promotions, so arithmetic stays at the declared width.
Floating types
The <float.h> macros give these minimum characteristics
(C17 5.2.4.2.2). In practice, almost every system uses IEC 60559 (IEEE 754) binary
formats, in which float is 32 bits and double is 64 bits. Annex F
ties C to that standard for implementations that define
__STDC_IEC_559__.
| Characteristic | float | double | long double |
|---|---|---|---|
Decimal digits of precision at least (FLT_DIG and so on) | 6 | 10 | 10 |
| Largest finite value at least | 1037 | 1037 | 1037 |
| Smallest positive normal value at most | 10−37 | 10−37 | 10−37 |
| Epsilon at most (difference between 1 and the next value) | 10−5 | 10−9 | 10−9 |
FLT_EVAL_METHOD tells whether floating expressions are evaluated at their own
type (0) or at double (1) or long double (2). A value of −1 means the
method is indeterminable.
C99 added float _Complex, double _Complex and
long double _Complex, with optional _Imaginary forms. These have
been optional since C11; __STDC_NO_COMPLEX__ shows they are absent. C23 adds
decimal floating types _Decimal32, _Decimal64 and
_Decimal128, also optional.
The void type and library types
void has no values and is always incomplete (C17 6.2.5). It is used for a
function that returns nothing, for an empty parameter list, and in void *,
a pointer to an object of unknown type.
Several types come from library headers:
| Type | Header | Meaning |
|---|---|---|
size_t | <stddef.h> | Unsigned type of the result of sizeof |
ptrdiff_t | <stddef.h> | Signed type of the difference of two pointers |
max_align_t | <stddef.h> | A type with the strictest fundamental alignment (C11) |
wchar_t | <stddef.h> | Wide character |
char8_t | <uchar.h> | UTF-8 code unit (C23) |
char16_t, char32_t | <uchar.h> | UTF-16 and UTF-32 code units (C11) |
nullptr_t | <stddef.h> | The type of nullptr (C23) |
Type qualifiers
| Qualifier | Meaning | Since |
|---|---|---|
const | The object is not modified through this name. Modifying a const object is undefined | C89 |
volatile | Every read and write is an observable side effect and must be carried out | C89 |
restrict | Only on pointers: objects reached through it are not also reached another way | C99 |
_Atomic | Operations on the object are atomic | C11 |
Writing a qualifier twice has the same effect as once (C17 6.7.3). A qualifier applies to
what is immediately to its left in the declarator, or to the base type if it is first, so
const int *p and int const *p are pointers to a constant
int, while int *const p is a constant pointer to
int.
A const object is not a constant expression in C. const int n = 5; int
a[n]; declares a variable length
array. constexpr (C23) is the way to declare a named constant usable in
constant expressions.
_Atomic is both a qualifier, as in _Atomic int, and a type specifier,
as in _Atomic(int). <stdatomic.h> supplies the operations
and the memory orders.
The meaning of restrict is on the
Arrays and pointers page.
typedef
A typedef declaration gives a type a new name. It has the form of a variable
declaration, with typedef in the storage-class position (C17 6.7.8). It
introduces an alias, not a new type: a typedef name and its original type are
interchangeable.
typedef unsigned long ulong;
typedef int vector[3]; /* vector is an array of three int */
typedef int (*compare)(int, int); /* compare is a pointer to a function */
typedef struct node node; /* node is struct node */
typedef reads like a declaration of a variable with that nameWriting typedef struct node node; means node can be used without
the struct keyword. The tag and the typedef name live in different
name spaces.
typeof and type inference (C23)
typeof(expression-or-type) names the type of its operand, with qualifiers
kept. typeof_unqual removes the qualifiers. In C23 auto in an
object definition with an initialiser infers the type from the initialiser. Many
compilers offered __typeof__ as an extension before this.
typeof(1 + 2.0) x; /* double */
const int c = 1;
typeof(c) a = 0; /* const int */
typeof_unqual(c) b; /* int */
auto n = 42u; /* unsigned int */
typeof, typeof_unqual and auto in C23Compatible types
Two types are compatible when they are the same, or when certain rules make them match (C17 6.2.7). For example, an enumerated type is compatible with its underlying integer type, and two structures in different translation units are compatible if their tags, members and member order match. Calling a function through a pointer to an incompatible type is undefined behaviour.
Conversions
C converts between arithmetic types implicitly, in the cases below. A cast requests a conversion explicitly.
Integer promotions
A type with rank no higher than int, or a bit-field of type
bool, int, signed int or unsigned int,
is converted to int if int can hold all its values, and to
unsigned int otherwise (C17 6.3.1.1). Promotions apply to the operands of
unary +, - and ~, to both operands of shifts, and as
part of the usual arithmetic conversions.
Integer conversion rank
boolhas the lowest rank.signed char,short,int,long,long longrank in that order.char,signed charandunsigned charhave the same rank.- Each unsigned type has the rank of its signed counterpart.
- An enumerated type has the rank of its compatible integer type.
- A standard integer type outranks an extended one of the same width.
The usual arithmetic conversions
These bring two operands to a common type (C17 6.3.1.8). They apply to
*, /, %, +, -, the
relational and equality operators, &, ^, |,
and the last two operands of ?:. The rules are checked in this order:
- If one operand is
long double, the other is converted tolong double. - If one is
double, the other is converted todouble. - If one is
float, the other is converted tofloat. - Otherwise both undergo the integer promotions, then:
- if they now have the same type, that type is used;
- if they have the same signedness, the one of lower rank is converted to the other;
- if the unsigned type’s rank is not lower, the signed operand is converted to the unsigned type;
- if the signed type can represent all values of the unsigned type, the unsigned operand is converted to the signed type;
- otherwise both are converted to the unsigned type of the signed operand’s rank.
unsigned int u = 1;
int i = -1;
if (i < u) ... /* false: i becomes UINT_MAX */
sizeof(int) > -1 /* false: -1 becomes SIZE_MAX */
What happens to the value
| Conversion | Result | Clause |
|---|---|---|
To bool | 0 if the value equals 0, otherwise 1 | 6.3.1.2 |
| Integer to unsigned type | Value modulo 2N if it does not fit | 6.3.1.3 |
| Integer to signed type that cannot hold it | Implementation-defined, or an implementation-defined signal | 6.3.1.3 |
| Floating to integer | The fractional part is discarded. Undefined if the whole part does not fit | 6.3.1.4 |
| Integer to floating | Exact if representable, otherwise rounded. Undefined if out of range | 6.3.1.4 |
| Floating to floating | Exact if the value fits. Rounded if the type is narrower. Undefined if out of range | 6.3.1.5 |
Lvalue, array and function conversions
When an expression is used for its value, an lvalue is read and loses its qualifiers. An
array becomes a pointer to its first element, and a function designator becomes a pointer
to the function, except in a few contexts such as sizeof and unary
& (C17 6.3.2.1). See
Arrays and pointers and
Lvalues.