Is it possible to use std :: uintptr_t to avoid the undefined behavior of out-of-bounds pointer arithmetic?

Now we know that doing out-of-bound-pointer-aithmetic arithmetic has undefined behavior, as described in this question https://stackoverflow.com/a/464829/2326 .

My question is: can we eliminate this restriction by performing a cast to std :: uintptr_t for arithmetic operations, and then return to the pointer? guaranteed to work?

For instance:

char a[5]; auto u = reinterpret_cast<std::uintptr_t>(a) - 1; auto p = reinterpret_cast<char*>(u + 1); // OK? 

Using the real world to optimize offset memory access - instead of p[n + offset] , I want to do offset_p[n] .

EDIT To make the question more explicit:

Given the base pointer p the char array, if p + n is a valid pointer, will reinterpret_cast<char*>(reinterpret_cast<std::uintptr_t>(p) + n) be guaranteed to get the same valid pointer?

+5
source share
2 answers

Yes, this is legal, but you must reinterpret_cast exact same uintptr_t value to return to char* .

(Therefore, what you intend to do is illegal, i.e. converting another value back to a pointer.)

5.2.10 Reinterpret

4. A pointer can be explicitly converted to any integer type large enough to hold it. The mapping function implementation is de defined.

5. The value of the integral type or enumeration type can be explicitly converted to a pointer. The converted pointer to an integer of suffix size (if one exists in the implementation) and back to the same type of pointer will have its original value;

(Note that there would be no way at all for the compiler to know that you subtracted one and then added it back.)

+4
source

No, uintptr_t cannot be used meaningfully to avoid undefined behavior when doing pointer arithmetic.

On the one hand, at least in C there is no guarantee that uintptr_t even exists. The requirement is that any value of type void* can be converted to uintptr_t and vice versa, which gives the original value without loss of information. In principle, there may not be some unsigned integer type wide enough to hold all pointer values. (I suppose the same applies to C ++, since C ++ inherits most of the C standard library and defines it by reference to the C standard.)

Even if uintptr_t exists, there is no guarantee that this arithmetic operation in the uintptr_t value does the same as the corresponding operation on the pointer value.

For example, I worked on systems (Cray, T90, and SV1 vector systems) on which byte pointers are implemented in software. A native address is a 64-bit address that refers to a 64-bit word; no hardware support for byte addressing. The char* or void* pointer consists of a 3-bit offset word pointer stored in unused, unused, high-order bits. The conversion between integers and pointers just copies the bit. Thus, incrementing a char* will cause it to point to the next 8-bit byte in memory; the increment a uintptr_t obtained by the conversion of a char* should have promoted it to indicate the next 64-bit word.

Here is just one example. More generally, conversions between pointers and integers are implementation specific, and the locale does not guarantee the semantics of these conversions (except, in some cases, converting back to a pointer).

So, you can convert the pointer value to uintptr_t (if this type exists) and perform arithmetic on it without risking undefined behavior, but the result may or may not be significant.

It happens that on most systems, matching between pointers and integers is easier, and you can probably get away with such a game. But you better use pointer arithmetic directly and just be careful to avoid any invalid operations.

+11
source

Source: https://habr.com/ru/post/974744/


All Articles