K
Keith Thompson
Shao Miller said:It seems to me that 'SIZE_MAX' is pretty useful as the count of
possible pointer values (not object representations) given a
contiguous range of object storage, and 'ptrdiff_t' simply allows for
a sign. But hey! Suppose the hardware supports far fewer pointer
values (for address registers, perhaps) than it does values for the
next-largest arithmetic register... If 'size_t' is a typedef to a
standard integer type, then we really need 'SIZE_MAX' rather than
relying on finding the maximum value of the standard integer type. An
arbitrary choice for an alternative to a missing 'SIZE_MAX' in a C89
implementation might yield unpleasant results when we increment a
pointer one too many times. Fortunately, the Standard evolves.
Note that SIZE_MAX is an upper bound on the value that can be yielded by
sizeof, not necessarily the least upper bound. There is no defined
constant (that I know of) that actually tells you the maximum size of an
object, which could be substantially smaller than SIZE_MAX. SIZE_MAX is
merely the maximum value of the unsigned type chosen to represent sizes.
(Consider the old m68000, which had 24-bit addresses but 16-bit and
32-bit integers.)
I don't fully understand why we have the optional 'intptr_t' and
uintptr_t' types, _except_ that the sign of 'ptrdiff_t' allows a
possibly greater bit-width than 'size_t', but 'uintptr_t' seems to
have a greater bit-width than 'intptr_t'. Oh well.
On many systems, uintptr_t is likely to be the same type as size_t, but
that assumes a monolithic address space. You might, for example, have
64-bit addresses and 16 exabytes of address space, but only up to
4-gigabyte objects (32 bits), so uintptr_t could be 64 bits while size_t
is only 32 bits.
It's also a matter of documentation; if you using uintptr_t, it's
clear that you're doing it because you want an unsigned integer
type that can represent addresses. (You'll also get a compile-time
error if there is no such integer type.)
There's no way to determine whether uintptr_t or intptr_t is preferred
for a given system, but I'm not sure what you'd do with that information
anyway.
I'd still be interested in anyone's response to:
In principle, objects themselves don't (necessarily) have types.
An object is merely a "region of data storage in the execution
environment, the contents of which can represent values". (Note:
"*can* represent", not "represents".) A type is imposed by the
lvalue (expression) used to refer to the object. For declared
objects, this is typically the type used in the object declaration;
for allocated objects, some type has to be imposed.
For any context where you care whether an object is an array or not,
you'll be using some type to access it. That type will clearly be
an array type or a non-array type.
I'm not at all sure that's sufficiently clear. If it isn't,
could you rephrase the question and/or provide an example?