I found 126.96.36.199.4 to be very clear about this all:
"The value of the result is implementation-defined, and its type (an unsigned
integer type) is size_t, defined in <stddef.h> (and other headers)."
7.17 Common definitions <stddef.h>
The following types and macros are defined in the standard header <stddef.h>.
Some are also defined in other headers, as noted in their respective
The types are [...]
which is the unsigned integer type of the result of the sizeof operator; [...]
And 188.8.131.52 from the Rationale is even more specific:
184.108.40.206 The sizeof operator
It is fundamental to the correct usage of functions such as malloc and fread
that sizeof(char) be exactly one. In practice, this means that a byte in C
terms is the smallest unit of storage, even if this unit is 36 bits wide; and
all objects are composed of an integer number of these smallest units. Also
applies if memory is bit addressable.
C89, like K&R, defined the result of the sizeof operator to be a constant of
an unsigned integer type. Common implementations, and common usage, have often
assumed that the resulting type is int. Old code that depends on this behavior
has never been portable to implementations that define the result to be a type
other than int. The C89 Committee did not feel it was proper to change the
language to protect incorrect code.
The type of sizeof, whatever it is, is published (in the library header
<stddef.h>) as size_t, since it is useful for the programmer to be able to
refer to this type. This requirement implicitly restricts size_t to be a
synonym for an existing unsigned integer type. Note also that, although size_t
is an unsigned type, sizeof does not involve any arithmetic operations or
conversions that would result in modulus behavior if the size is too large to
represent as a size_t, thus quashing any notion that the largest declarable
object might be too big to span even with an unsigned long in C89 or uintmax_t
in C99. This also restricts the maximum number of elements that may be
declared in an array, since for any array a of N elements,
N == sizeof(a)/sizeof(a)
Thus size_t is also a convenient type for array sizes, and is so used in
several library functions.
C89 specified that sizeof’s operand can be any value except a bit-field, a
void expression, or a function designator. This generality allows for
interesting environmental inquiries. Given the declarations
int *p, *q;
these expressions determine the size of the type used for:
sizeof(F(x)) // ... F’s return value
sizeof(p-q) // ... pointer difference
(The last type is available as ptrdiff_t in <stddef.h>.)
With the addition of variable length arrays (§220.127.116.11) in C99, the sizeof
operator is a constant expression only if the type of the operand is not a
variable length array type. However, the notion of “size” is consistently
maintained for important operations such as pointer increment, subscripting,
and pointer difference. That is, it is still possible to determine the number
of elements in a variable length array with
sizeof(vla) / sizeof(vla)
Finally, sizeof can still be used in an argument to the malloc function.
QUIET CHANGE IN C99
With the introduction of the long long and extended integer types, the sizeof
operator may yield a value that exceeds the range of an unsigned long.
As IEEE Std 1003.1, 2004 further adds:
The implementation shall support one or more programming environments in which
the widths of ptrdiff_t, size_t, and wchar_t are no greater than the width of
type long. The names of these programming environments can be obtained using
the confstr() function or the getconf utility.
C99 with TC2 further adds for 7.17:
The types used for size_t and ptrdiff_t should not have an integer conversion
rank greater than that of signed long int unless the implementation supports
objects large enough to make this necessary.