An example of a safe pointer value

I am using working draft N3797.

Section 5.7 / 1 says:

[...] To add, both operands must be arithmetic or an unenumerated enumeration type, or one operand must be a pointer to a fully qualified object and the other must be an integral or unenumerated enumeration type.

Ok. But consider the rule from section 3.7.4.3/3:

- the result of an additive or bitwise operation, one of the operands is an integer representation of a safe pointer value P

, if the result converted to reinterpret_cast<void*>

will compare equal to a safe pointer that can be computed from reinterpret_cast<void*>(P)

.

That is, we cannot apply pointer arithmetic to a pointer to void

. Could you give an example that reflects the rule from 3.7.4.3 ?

A similar question does not provide a suitable example, because here is a pointer to empty arithmetic entries.

+3


source to share


2 answers


Garbage collection implementation must keep track of pointers. In practice, this has often been done using a memory map as a key, so any words of the size of a pointer in memory within a specific numeric range are considered bona fide pointers and thus prevent garbage collection of an allocator including a given address value. This strategy is imprecise and C ++ strives for the best, but it also supports existing GC programs.

Contrary to your intuition, the bullet does allow "pointer-to" arithmetic void*

. An integer ( std::intptr_t

) derived from a pointer to reinterpret_cast

must also be tracked as a pointer, and the object continues to be affected by garbage collection as such, even if modified by append or bitwise operations.



std::uintptr_t handle1 = reinterpret_cast< std::uintptr_t >( new foo );
std::uintptr_t handle2 = reinterpret_cast< std::uintptr_t >( new foo );
// handle1 and handle2 won't be collected, despite no pointer-type references.

std::uintptr_t diff = handle2 - handle1; // not a pointer

std::uintptr_t sum = handle1 + diff; // sum refers to handle2
handle2 = 0; // Invalidate original reference to handle2

// At this point, the second foo is still reachable via sum.

      

Strong pointer safety improves the status quo, but false positives are still possible when operations that match the rule do not generate the actual pointer representation. For example, it is not clear from the standard text if it is tracked diff

because it is the result of a difference operation with two safe derived operands, as opposed to adding with only one safe derived operand. (Whether or not it is somewhat inconsequential, false positives will still occur.)

0


source


All that 3.7.4.3/3 says is that if you have a "safe derived pointer integer representation" and do the math with it, the result will be a valid "pointer safe derived integer representation" if you could come to the same result with pointer arithmetic and casting alone.

While you are not allowed to do arithmetic on a directive void*

, there are various other ways to get a valid pointer from void*

. While I'm too lazy to support each step with quotes from the standard, the following example should be given:

double arr[10];
double* P = &arr[0]; // safely derived pointer
intptr_t N = reinterpret_cast<intptr_t>(P); // valid integer repr

void* V = reinterpret_cast<void*>(P);

// Compute &a[1] from V
void* V2 = reinterpret_cast<void*>(
    static_cast<char*>(V) + sizeof(double));

// Do the same with N
intptr_t N2 = N + sizeof(double);

assert(reinterpret_cast<void*>(N2) == V2);

      



  • V2 is a safely derived pointer that can be computed from reinterpret_cast<void*>(P)

  • N2 is the result of an additive or bitwise operation, one of the operands (N) is an integer representation of the safe value of the pointer P

Since V2 and N2 compare equal, N2 is also a "safe derived pointer integer representation".

+2


source







All Articles