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

2

Consider:

struct T{};

int main() {
    T* p = (T*)0xDEADBEEF;
}

Using an invalid pointer is implementation-defined. Dereferencing it is undefined behavior. My question isn't about those.

My question is whether the mere initialization of p, as is, is defined.

If you think you already have all the information needed to answer this question (or if you found out that this is a duplicate), you need read no further. Following is some mumbling based on my findings:

The C standard (of which the C++ standard is based on) says:

6.3.2.3 Pointers

5   An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

Which hints it may be implementation defined.

The C++ Standard only defines (as far as I know) that any use of an invalid pointer value is implementation defined. The footnote is of special importance, as it seems to suggest that the mere copy of such value is already an use of the pointer. (Or does it mean the pointed-to value? I'm confused)

6.7 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 (6.9.2). 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.37

37 Some implementations might define that copying an invalid pointer value causes a system-generated runtime fault

This kind of agrees with the C standard. The problem is I'm not convinced this is an example of an invalid pointer value, as the standard clearly says this type of value is caused by the end of the duration of storage for that address being reached (which clearly never happened).

There are also many instances of pointer arithmetic that are Undefined BehaviourTM, but clearly no arithmetic or manipulation on the value of the pointer is being done here. This is merely an initialization.

c++
pointers
language-lawyer
asked on Stack Overflow May 27, 2018 by Cássio Renan • edited May 27, 2018 by Cássio Renan

2 Answers

4

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.

answered on Stack Overflow May 27, 2018 by Davis Herring • edited May 28, 2018 by Cássio Renan
1

Speaking from my experience with desktop (UNIX, Linux, Windows) applications, I think you can assign any value to a pointer. If you don't dereference the pointer, these systems don't cause strange behavior when you assign such values to a pointer.

The following example show one mechanism I have seen to deal with saving pointers to disk and restoring pointers from disk.

Let's take a simplified view of the connections between faces, edges, and vertices of a CAD model.

struct Face;
struct Edge;
struct Vertex;

struct Face
{
   std::vector<Edge*> edges;
};

struct Edge
{
   std::vector<Face*> faces;
   Vertex* start;
   Vertex* end;
};

struct Vertex
{
   std::vector<Edge*> edges;
   double x;
   double y;
   double z;
};

and you have a face in the XY plane.

       E3
V4 +--------+ V3
   |        |
E4 |    F   | E2
   |        |
   +--------+
 V1     E1   V2

Such a face could be saved to disk using the following format.

0 Face 4 $1 $2 $3 $4
1 Edge 1 $0 $5 $6
2 Edge 1 $0 $6 $7 
3 Edge 1 $0 $7 $8
4 Edge 1 $0 $8 $5
5 Vertex 2 $1 $4 0 0 0 
6 Vertex 2 $2 $1 10 0 0 
7 Vertex 2 $3 $2 10 10 0 
8 Vertex 2 $4 $3 0 10 0 

where the first number indicates an index in an array of objects while the fields that have $ prefix is a pointer to the item at that index.

When that information is read from disk, there are two passes to restore the objects to a usable state. In the first pass, the indices are stored in place of pointers. In the second pass, the indices are converted to pointers. In the first pass, the numbers [0 - 8] are stored where pointers are expected. Only after the second pass will the pointers point to the appropriate objects.

Long story sort, the pointer member variables are assigned values that are obviously not valid pointers but the mechanism works flawlessly.

Whether that would be a problem for other platforms, I cannot comment. I have no experience to fall back on.

answered on Stack Overflow May 27, 2018 by R Sahu • edited May 28, 2018 by R Sahu

User contributions licensed under CC BY-SA 3.0