Is checking the value of a dangling pointer safe or Undefined Behavior? [duplicate]
Asked Answered
L

3

11

We can only de-reference a valid pointer and we can only check the address that a dangling built-in pointer points to. We cannot access its value (the value in the address of object it is pointing to).

int* ptr = nullptr;
if(ptr) // != 0x00000000
   std::cout << *ptr << '\n';

ptr = new int(1000);

if(ptr) // != 0x00000000
   std::cout << *ptr << '\n';

delete ptr; // still pointing at the address of that dynamic object but that object has been destroyed.

if(ptr) // succeeds or undefined behavior?
   std::cout << *ptr << '\n'; // of course UB here

So it is clear for me but what matter me only is whether checking a pointer value is safe or yields UB? if(ptr). Because let's assume that I didn't access the value in that address like in std::cout << *ptr.

Larimer answered 30/9, 2021 at 19:28 Comment(3)
Checking whether a pointer is nullptr unfortunately does not check whether the pointed-to object has been destroyed or not. (Obviously, if the pointer is nullptr there is no pointed-to object, but the flip side does not hold.)Poddy
@fabian: Thanks a lot! it was my bad I've forgotten the pointer operator,Larimer
It's implementation defined behavior since C++14, undefined before C++14: https://mcmap.net/q/795466/-pointers-in-c-after-deleteAlter
L
4

Is checking the value of a dangling pointer safe or Undefined Behavior?

It's not UB (since C++14), but "safe" depends on what you expect. There is no guarantee about the result of such check. It could be true or false. Assuming that a pointer is valid based on if(ptr) is not safe in general.

Lour answered 30/9, 2021 at 19:39 Comment(2)
Looks like it was UB: https://mcmap.net/q/795466/-pointers-in-c-after-deleteRianon
@chux-ReinstateMonica Edited.Lour
L
4

It is safe to dereference a non-null pointer only if it is actually pointing at a valid object, and unfortunately there is no way to test for that condition in C/C++ 1.

1: unless you manually keep track of the addresses of your valid objects and can thus search for the pointed-at address in your own tracking data.

It is not undefined behavior to test whether a pointer is equal to null or not. However, per [basic.stc.general], apparently after a block of memory is destroyed/reclaimed, any use of any pointer value referring to any part within that block is implementation-defined behavior:

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.

So, some people may argue that it means a pointer holding the address of a destroyed object MAY OR MAY NOT even be legal to compare against other pointers or even nullptr itself, since the address is invalid. Only the compiler gets to decide if that is legal or not.

Largish answered 30/9, 2021 at 19:46 Comment(5)
not perfectly well-defined, rather implementation-defined. See comment on the other answerScummy
Checking if a pointer is equal to null or not is a well-defined operation. What you do with that pointer is a different matter.Largish
@RemyLebeau The wording of the standard is "Any other [than dereferencing] use of an invalid pointer value has implementation-defined behavior.", is there a separate section relating to nullptr comparisons that contradict this?Ostrich
@Frank if (ptr) is essentially just shorthand for if (ptr != 0) or if (ptr != nullptr). The standard in [conv.ptr] says: "A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type... Two null pointer values of the same type shall compare equal." I read that as saying that if ptr has a null value then the comparison is guaranteed to be true, otherwise if it does not have a null value then the comparison is guaranteed to be false. What section are you looking at?Largish
@RemyLebeau [new.delete.single] for delete rendering the pointer "invalid", and [basic.stc.general] for using an invalidated pointer being implementation-defined (which is still well-defined, just not portably safe).Ostrich
D
0

Yes, for int* ptr;, the expression ptr is always safe. The behaviour of the program remains defined. But

  • The specific value of a pointer is implementation-defined. [Ellipsis , emphasis mine]

[6.8.2.3.4] A value of a pointer type that is a pointer to or past the end of an object represents the address of the first byte in memory (6.7.1) occupied by the object or the first byte in memory after the end of the storage occupied by the object, respectively. [...] The value representation of pointer types is implementation-defined. Pointers to layout-compatible types shall have the same value representation and alignment requirements (6.7.6). [Note: Pointers to over-aligned types (6.7.6) have no special representation, but their range of valid values is restricted by the extended alignment requirement. — end note]

  • nullptr is guaranteed to be equal(==) to 0 and NULL.
  • In boolean context, nullptr evaluates to false, ALL other values, no matter whether they represent an address of valid objects, evaluate to true.
  • delete does not modify the pointer. EDIT: Or maybe it does, see the discussion in comments.
  • Pointers have same initialization rules as integers, meaning locals, non-static members are not initialized. int* ptr; assert(ptr==nullptr); does NOT hold in general. But global pointers are zero-initialized and thus evaluate to false at the start.

All taken together, although the program is safe, it is quite easy to make it nondeterministic at best.

Defecate answered 30/9, 2021 at 19:52 Comment(6)
"delete does not modify the pointer" is not necessarily true as far as I know.Poddy
Answer has int ptr;, yet code is int *ptr;. Is that intended?Rianon
@NathanPierson But how can it modify the pointer? AFAIK the signature is void operator delete(void* ptr) - takes a copy of the pointer, so it cannot modify the variable itself, right?Defecate
@chux-ReinstateMonica No, it's not. Thank you, in the future feel free to fix any typos in my answers :)Defecate
@Defecate I think it's the difference between the delete expression and the operator delete function that gets invoked. It may be a pretty academic point anyway if compilers are permitted to do so by the standard but, in practice, do not elect to do so.Poddy
@NathanPierson You might be right, the standard in 7.6.2.8 calls the latter as deallocation function. So there is a difference, although the chapter does not state anything about the value after delete. Sadly.Defecate

© 2022 - 2024 — McMap. All rights reserved.