2
 #include <iostream>
 int main()
 {
    int *ptr = NULL;
    // It does not crash
    *ptr; --------> Point-1
    //But this statment crashed
    std::cout<<"Null:"<<*ptr<<"\n"; ------> Point-2
    return 0;
 }

In the above code when i comment "Point-2" the code is not crashed. But when i un-comment the "Point-2" it is crashed. Since ptr is NULL ideally the Point-1 should also crash. Please corret me if i am wrong. Can someone explain me why the code not crashed when i simply dereffernece the pointer ?

Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
sagar
  • 367
  • 2
  • 12
  • 2
    Compile in debug mode with no optimizations and try again.. – BlackBear Apr 27 '16 at 09:12
  • 2
    Stop assuming invoking *undefined behavior* results in *defined behavior*. It doesn't. The fact is, if you're *lucky*, it crashes, and you really gotta want want to see it to understand why that's true. – WhozCraig Apr 27 '16 at 09:17
  • 2
    For your reference :- http://stackoverflow.com/questions/12645647/what-happens-in-os-when-we-dereference-a-null-pointer-in-c – ravi Apr 27 '16 at 09:18
  • Even disregarding the presence of undefined behaviour, `*ptr;` has no obervable effect, so the compiler is free to remove it. – molbdnilo Apr 27 '16 at 09:24
  • @WhozCraig so basically its an undefined behaviour. – sagar Apr 27 '16 at 09:30
  • @BlackBear Yes i have tried with -g and O0. Still it didnt crashed. – sagar Apr 27 '16 at 09:31
  • @ravi, i understand whatever explained in the link provided. However my question is in different context. – sagar Apr 27 '16 at 09:32
  • @sagar yes, but as molbdnilo pointed out, it is an unused expression with no side-effects, so the compiler can (and likely will) completely throw it out anyway. Doesn't mean you should rely on that, just like you shouldn't rely on it crashing. – WhozCraig Apr 27 '16 at 09:33
  • 1
    We need a canonical question "Why does code that invoke undefined behavior behave in a way that surprises me?" with an answer "Because it can." – Tadeusz Kopec for Ukraine Apr 27 '16 at 09:34
  • 1
    @WhozCraig And the reason the compiler can throw out an unused expression with no side-effects in this case is because dereferencing a NULL is UB. Were it not, the expression would have possible side-effects. (And that's also the reason dereferencing a NULL is UB. We want that optimization to be possible.) – David Schwartz Apr 27 '16 at 10:04

2 Answers2

2

Dereferencing a null pointer is undefined behavior. Undefined behavior is not equal to error. Anything may happen if you triggered undefined behavior. If you are asking the other way:

Why is undefined behavior not causing an error instead of giving us strange behavior?

It may be due to many reason. One reason is performance. For example, in the implementation of std::vector (at least in MSVC), there is no check if the index is out of range in Release Mode. You can try to do this:

std::vector<int> v(4);
v[4]=0;

It will compile and run. You may got strange behavior or you may not. However, in Debug mode it will throw exception at run-time. MSVC does the check in Debug Mode because performance is not important in Debug Mode. But it does not in Release Mode because performance matters.

The same applies for dereferencing a null pointer. You can image that the code of dereferencing will be put in a wrapper like this:

//Imaginary code
T& dereference(T* ptr){
    if(ptr==nullptr){
        throw;
    }
    return *ptr;
}

This part: if(ptr==nullptr){throw;} will slow the dereferencing process of every pointer in the context which is not desirable.

However, it may be done like this:

//Imaginary code
T& dereference(T* ptr){
    #ifdef DEBUG
    if(ptr==nullptr){
        throw;
    }
    #endif
    return *ptr;
}

I think you got the idea now.

Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
0

In point 2 you try to display contents of 0x0 address, which generates access violation error.

In point 1, you do nothing with it, so the program doesn't have to access memory described by this pointer. This generates no acces violation error.

  • Can't see why this has been down-voted. I think it is exactly right. – Martin Bonner supports Monica Apr 27 '16 at 09:54
  • @MartinBonner It doesn't explain *why* the program doesn't have to access the memory. If, for example, dereferencing a NULL pointer had some defined behavior, optimizing by removing the access would be illegal (because you wouldn't get behavior you were supposed to get). It's because it's UB that the access can be removed. – David Schwartz Apr 27 '16 at 10:03
  • 1
    @DavidSchwartz: That is wrong. The access can be optimized out because dereferencing a pointer to non-`volatile` has no side effects. It has nothing to do with UB at all. If the code had been `int i=42, *ptr=&i; *ptr;`, the compiler could still have optimized out the access. – Martin Bonner supports Monica Apr 27 '16 at 10:07
  • @MartinBonner Right, because it's UB. If, for example, dereferencing a NULL was guaranteed to cause a crash, then it would have side-effects, the crash, and so couldn't be optimized out. It's because dereferencing a NULL is UB that there are no side effects. (Likewise dereferencing other invalid pointers is UB.) – David Schwartz Apr 27 '16 at 10:13