简体   繁体   中英

What is the motivation for ptrdiff_t?

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

Is there ever a time when intmax_t does not equal ptrdiff_t ?

Yes, on 32-bit platforms where pointers are 32-bit, and intmax_t is >=64-bit.

Here is the relevant paragraph in the C Standard:

6.5.6 Additive operators
[...]
Semantics
[...]
9. When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. The size of the result is implementation-defined, and its type (a signed integer type) is ptrdiff_t defined in the <stddef.h> header. If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions P and Q point to, respectively, the i -th and j -th elements of an array object, the expression (P)-(Q) has the value i - j provided the value fits in an object of type ptrdiff_t . Moreover, if the expression P points either to an element of an array object or one past the last element of an array object, and the expression Q points to the last element of the same array object, the expression ((Q)+1)-(P) has the same value as ((Q)-(P))+1 and as -((P)-((Q)+1)) , and has the value zero if the expression P points one past the last element of the array object, even though the expression (Q)+1 does not point to an element of the array object. 108)


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 as size_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 and ptrdiff_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 for ptrdiff_t and unsigned for size_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 as size_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 of int32_t , hence does fit in the ptrdiff_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 type ptrdiff_t .

The rationale for using an implementation defined type ptrdiff_t and not long 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 type int was used for this purpose and when size_t was introduced with a potentially different range than unsigned int , a specific type for the difference of 2 pointers became necessary as int was too small. ptrdiff_t may still be too small but only for border cases.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM