Why does C define a separate type for the difference between two pointers? For example, why couldn't this be defined as long long
, or even doing intmax_t
? Is there ever a time when intmax_t
does not equal ptrdiff_t
?
Basically, what is the meaning why this typedef (with a specific string format, etc.) is useful? And as a basic example of use, I'm doing:
int a = 1, b = 2;
printf("%td XXX\n", (char *)(&b) - (char *)(&a)); // 't' is formatter for prtdiff_t
Here is the relevant paragraph in the C Standard:
108) Another way to approach pointer arithmetic is first to convert the pointer(s) to character pointer(s): In this scheme the integer expression added to or subtracted from the converted pointer is first multiplied by the size of the object originally pointed to, and the resulting pointer is converted back to the original type. For pointer subtraction, the result of the difference between the character pointers is similarly divided by the size of the object originally pointed to. When viewed in this way, an implementation need only provide one extra byte (which may overlap another object in the program) just after the end of the object in order to satisfy the “one past the last element” requirements.
Although the C Standard does not mandate this,
ptrdiff_t
is usually defined as the signed integer type with the same size assize_t
. On architectures where the maximum size of an object is limited to 32 bits, such as 32-bit Windows and Unix systems,size_t
is an unsigned 32-bit integer type andptrdiff_t
a signed 32-bit integer type, whereas on 64-bit systems with a larger address space, they both are 64-bit integer types, signed forptrdiff_t
and unsigned forsize_t
.Note however that the difference of 2 pointers into a very large array of
char
may exceed the range of the signed type with the same size assize_t
. For example a 3GB char array may be available on a 32-bit system (UINT32_MAX
> 3GB) but the difference of 2 pointers can be as large as 3GB in absolute value, which exceeds the range ofint32_t
, hence does fit in theptrdiff_t
type of this platform, invoking undefined behavior.Most implementations obtain the difference by subtracting the addresses and dividing the result by the element size, using signed arithmetics or signed right shift if the size is a power of 2. This may produce an incorrect result for large arrays of elements larger than
char
even if the value fits in an object of typeptrdiff_t
.The rationale for using an implementation defined type
ptrdiff_t
and notlong long
is to keep compatibility with existing implementations and avoid using larger types on systems with a smaller address space, where this would cause extra overhead in both space and time. In early C implementations, the typeint
was used for this purpose and whensize_t
was introduced with a potentially different range thanunsigned int
, a specific type for the difference of 2 pointers became necessary asint
was too small.ptrdiff_t
may still be too small but only for border cases.