Is a Pointer with the Right Address and Type Still Always a Valid Pointer Since C++17

Is a pointer with the right address and type still always a valid pointer since C++17?


Is this interpretation of this modification of the standard right or are there other rules that compensate the deletion of this cited sentence?

Yes, this interpretation is correct. A pointer past the end isn't simply convertible to another pointer value that happens to point to that address.

The new [basic.compound]/3 says:

Every value of pointer type is one of the following:

(3.1)
a pointer to an object or function (the pointer is said to point to the object or function), or

(3.2)
a pointer past the end of an object ([expr.add]), or

Those are mutually exclusive. p1+1 is a pointer past the end, not a pointer to an object. p1+1 points to a hypothetical x[1] of a size-1 array at p1, not to p2. Those two objects are not pointer-interconvertible.

We also have the non-normative note:

[ Note: A pointer past the end of an object ([expr.add]) is not considered to point to an unrelated object of the object's type that might be located at that address. [...]

which clarifies the intent.


As T.C. points out in numerous comments (notably this one), this is really a special case of the problem that comes with trying to implement std::vector - which is that [v.data(), v.data() + v.size()) needs to be a valid range and yet vector doesn't create an array object, so the only defined pointer arithmetic would be going from any given object in the vector to past-the-end of its hypothetical one-size array. Fore more resources, see CWG 2182, this std discussion, and two revisions of a paper on the subject: P0593R0 and P0593R1 (section 1.3 specifically).

Using std::launder to validate non pointer to object pointer value since C++17

No. The bytes constituting the int object p2 points to are not reachable through p1+1.


The "reachable" rule basically means that launder doesn't allow you to access storage you can't legally access via the original pointer. Since an opaque function may launder pointers as much as it wants, permitting this kind of shenanigans would substantially inhibit escape analysis.

C++ standard conforming method to assign address of program memory to pointer

There is no standards-compliant way of doing this. Standard C++ does not have a notion of memory layout, nor of particular integers being meaningful when casted to pointers (other than those which came from casting pointers to integers).

The good news is, “undefined behavior” is undefined by the standard. Implementations are free to offer guarantees that certain types of otherwise-UB code will do something meaningful. So if you want guaranteed correctness, rather than “just happened to work”, you won’t get that from the Standard but you may get it from your compiler documentation.

For literally all C++ compilers I know of, using reinterpret_cast as you’ve done here will do what you expect it to do.

An issue about converting a pointer to char to another type pointer


Does the code at #f have undefined behaviour due to tptr does not point to an object of type Test?

The program has undefined behavior since #a, because malloc is defined as triggering implicit object creation and returning a pointer to a suitable created object, but the set of objects which would give the rest of the program defined behavior is empty.

Assign a pointer with the address of different data type without casting


ClassA *ptrA = &objB; // Question1

Why this assignment is valid?

Because that's how polymorphic inheritance works. A ClassB is a ClassA; so a reference/pointer to ClassB can convert to a reference/pointer to ClassA.

ptrA = (ClassA*)&objB;

That's doing the same thing, but making the cast explicit. This is much more dangerous though - the evil C-style cast will allow any pointer conversion, whether or not it's valid, while the original implicit conversion will only allow safe conversions (as the derived-to-base pointer conversion is).

ptrA = new classB;

That creates a dynamic object, giving you a pointer to that; the original sets the pointer to point to an existing object. Don't use new unless you really need it.

ClassA objA = *ptrA // Question2

This is sometimes known as slicing. If the base class is copyable (as it presumably is here), then the base sub-object can be copied to make a new object of that type. Technically, *ptrA is converted to a reference to ClassA (since such a conversion is allowed, just as the previous pointer conversion is); then that reference is used to copy-initialise objA.

Slicing can cause confusion; but it's not an issue if you only use abstract base classes, since they can't be instantiated directly.

Is initializing a pointer with an arbitrary, literal, non-zero value defined?

Your C-style cast is performing a reinterpret_cast; casting an arbitrary integer value like this is implementation-defined:

8.5.1.10 Reinterpret cast

5   A value of integral type or enumeration type can be explicitly converted to a pointer. A pointer converted to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type will have its original value; mappings between pointers and integers are otherwise implementation-defined. [ Note: Except as described in [basic.stc.dynamic.safety], the result of such a conversion will not be a safely-derived pointer value. — end note ]

If the result is (deemed by the implementation to be) an invalid pointer value, then it’s probably again implementation-defined what happens when it is stored in a variable, but that’s less clear:

6.6.4 Storage duration

4   When the end of the duration of a region of storage is reached, the values of all pointers representing the address of any part of that region of storage become invalid pointer values.
Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have undefined behavior.
Any other use of an invalid pointer value has implementation-defined behavior.



Related Topics



Leave a reply



Submit