Is D's pointer subtraction more permissive than C (and C++)?

Steven Schveighoffer schveiguy at gmail.com
Fri Apr 1 17:39:24 UTC 2022


On 4/1/22 11:52 AM, Ali Çehreli wrote:
> As the following quote from a Microsoft document claims, and as I've 
> already known, pointer subtraction is legal only if the pointers are 
> into the same array: "ANSI 3.3.6, 4.1.1 The type of integer required to 
> hold the difference between two pointers to elements of the same array, 
> ptrdiff_t." ( 
> https://docs.microsoft.com/en-us/cpp/c-language/pointer-subtraction?view=msvc-170 
> )
> 
> I suspect "array" means "a block of memory" there because arrays are 
> ordinarily malloc'ed pieces of memory in C.

I'm assuming this has to do with the ability to detect artifacts of how 
the compiler/library lays out memory, which shouldn't really figure into 
program behavior.

In practice, I don't see how it affects the behavior *of the compiler*. 
When you subtract two pointers, I don't see how the compiler/optimizer 
can make some other decision based on the subtraction not being between 
two pointers to the same block of memory.

> 
> 1) Is D more permissive (or anemic in documentation:))? I ask because 
> paragraph 5 below does not mention the pointers should be related in any 
> way:
> 
>    https://dlang.org/spec/expression.html#pointer_arithmetic

I assume this is because nobody thought about it? But I don't see a 
problem with omitting that requirement.

> 
> 2) Is subtracting pointers that used to be in the same array legal.
> 
> void main() {
>    auto a = [ 1, 2 ];
>    auto b = a;
>    assert(a.ptr - b.ptr == 0);    // i) Obviously legal?
> 
>    // Drop the first element
>    a = a[1..$];
>    assert(a.ptr - b.ptr == 1);    // ii) GC-behaviorally legal?
> 
>    // Save the pointer
>    const old_aPtr = a.ptr;
>    // and move the array to another memory
>    a.length = 1_000_000;
>    // Expect a and b are on different blocks of memory
>    assert(a.ptr != old_aPtr);
> 
>    assert(old_aPtr - b.ptr == 1);  // iii) Practically legal?
> }

Assuming C rules, I still think all this is legal. I'd even hazard to 
guess it's legal to do this:

```c
struct S
{
    int arr1[5], arr2[5];
}
void foo() {
    S s;
    ptrdiff_t p = &s.arr1[0] - &s.arr2[0];
}
```

Because you know the relationship between the pointers is defined. I.e. 
this is NEVER going to change from run to run, or build to build.

> 
> Regardless of your answer, I will go ahead and perform that last 
> subtraction :).
> 
> Ali
> 
> P.S. I am trying to implement a type where slices will follow the 
> elements as the elements may be moved in memory:
> 
>    const old_aPtr = a.ptr;
> 
>    a ~= e;
>    if (a.ptr != old_aPtr) {
>      // Elements are moved; adjust the slice
>      assert(b.ptr >= old_aPtr);
>      const old_bOffset = b.ptr - old_aPtr;
>      b = a[old_bOffset .. $];
>    }
> 
> If you ask why I don't keep offsets instead of slices to begin with, I 
> want to use pointers (implicitly in D slices) so that they participate 
> in the ownership of array elements so that the GC does not free earlier 
> elements as the buffer is popFronted as well.

This should be fine. I would suggest to store things as offsets anyway, 
and have accessors for the pointers.

-Steve


More information about the Digitalmars-d mailing list