Home > Back-end >  What is the Difference between accessing a Pointer whose value is Null and accessing what it points
What is the Difference between accessing a Pointer whose value is Null and accessing what it points

Time:05-20

  • I've heard that accessing a pointer whose value is null is safe since you are not setting any data to it or from it, you are just accessing it.

  • But I also heard that accessing what it points to (when it's null) isn't safe, why is that?

  • If you are accessing to what it points to (when it's null) aren't you accessing nothing?

  • I think that there shouldn't be any issues with that unless you are setting values to something from it.

  • I've heard that from many people tho I never experienced any crashes or bugs related to that (when reading data from inside a pointer that is null), when I catch an exception I just let it be since I'm not setting any data from it to something. Is that ok?

int x; 
int* px = &x; 
int* pn = nullptr; 
if (px==px) { do something;} 

CodePudding user response:

By accessing I mean dereferencing

Dereferencing a null pointer is undefined behavior.

I never experienced any crashes or bugs related to that

The program has undefined behavior meaning it is still in error even if it doesn't say so explicitly and "seems to be working".

Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely(or make conclusions based) on the output of a program that has undefined behavior. The program may just crash.

So the output that you're seeing(maybe seeing) is a result of undefined behavior. And as i said don't rely on the output of a program that has UB. The program may just crash.

So the first step to make the program correct would be to remove UB. Then and only then you can start reasoning about the output of the program.


1For a more technically accurate definition of undefined behavior see this where it is mentioned that: there are no restrictions on the behavior of the program.

CodePudding user response:

The sample code (as it's exposed by OP in this moment) got a bit confusing.

Thus, I would like to add some examples to the accepted answer what's allowed and what not:

#include <iostream>

int main()
{
  int x = 0; // make some storage
  int* px = &x; // px initalized with address of x     -> OK.
  int* pn = nullptr; // pn initialized with nullptr    -> OK.
  if (px == px) { /* ... */ } // senseless but         -> OK.
  if (px == pn) { /* ... */ } //                       -> OK.
  std::cout << *px; // dereference a valid pointer     -> OK.
  std::cout << *pn; // dereference a null pointer      -> UNDEFINED BEHAVIOR!
  px = pn; // assign a (null) pointer                  -> OK.
  std::cout << *px; // dereference a null pointer      -> UNDEFINED BEHAVIOR!
  // ...and finally a common bug...
  int* py = nullptr; //                                -> OK.
  { int y = 1; // storage with limited life-time       -> OK.
    py = &y; // assign address of storage              -> OK.
  } // scope end -> life-time of y ends                -> OK.
  // Attention! This makes py dangling (pointing to released storage).
  if (py /* != nullptr*/) { // This doesn't help. py is not a null pointer.
    std::cout << *py; // access after end of life-time -> UNDEFINED BEHAVIOR!
  }
}

Output of Compiler (g 11.2):

g   -std=c  17 -O2 -Wall -pedantic -pthread main.cpp # && ./a.out # Compile but don't exec. It contains UNDEFINED BEHAVIOR!
main.cpp: In function 'int main()':
main.cpp:8:10: warning: self-comparison always evaluates to true [-Wtautological-compare]
    8 |   if (px == px) { /* ... */ } // senseless but         -> OK.
      |       ~~ ^~ ~~

Demo on coliru

  • Related