Search code examples
clanguage-lawyerptrdiff-t

Usage of ptrdiff_t


I'm implementing iterator through continuous chunk of memory and came to the issue about its conforming usage. My current implementation (assuming I'm iterating through array of chars).

typedef struct iterator{
    void *next_ptr;
    void *limit; //one past last element pointer
} iterator_t;

void *next(iterator_t *iterator_ptr){
    void *limit = iterator_ptr -> limit;
    void *next_ptr = iterator_ptr -> next_ptr;
    ptrdiff_t diff = limit - next_ptr;
    if(diff <= 0){
        return NULL;
    }
    iterator_ptr -> next_ptr = ((char *) next_ptr) + 1;
    return next_ptr;
}

The issue is the Standard claims at 6.5.6(p9) that:

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

This is true. I assume the area I'm iterating through is an array.

If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions 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.

The limits of ptrdiff_t are defined at 7.20.3(p2):

limits of ptrdiff_t

PTRDIFF_MIN −65535

PTRDIFF_MAX +65535

There is no any guarantees that all values represented with size_t should be represented with ptrdiff_t.

So we judging by the limits we can conformingly subtract pointers of an array only of 65535 elements at most? So this would not work in general case where I want to subtract two pointers to elements of an array of unknown size?


Solution

  • This seems to be a problem in the C standard itself.

    As you noted, 6.5.6 Additive operators, paragraph 9 states, in part:

    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. ...

    There appears to be no guarantee in the C standard that you can represent the difference of two pointers in a ptrdiff_t.

    Realistically, this would mean that a ptrdiff_t has to be larger than a size_t. A size_t only has to cover magnitude in a fixed number of bits. ptrdiff_t has to cover both magnitude and direction. If sizeof( size_t ) == sizeof( ptrdiff_t ), then there's no guarantee that the undefined behavior in 6.5.6p9 won't be invoked.