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.
>- 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:
I assume this is because nobody thought about it? But I don't see a problem with omitting that requirement.
>- 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:
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