When Does Invoking a Member Function on a Null Instance Result in Undefined Behavior

When does invoking a member function on a null instance result in undefined behavior?

Both (a) and (b) result in undefined behavior. It's always undefined behavior to call a member function through a null pointer. If the function is static, it's technically undefined as well, but there's some dispute.


The first thing to understand is why it's undefined behavior to dereference a null pointer. In C++03, there's actually a bit of ambiguity here.

Although "dereferencing a null pointer results in undefined behavior" is mentioned in notes in both §1.9/4 and §8.3.2/4, it's never explicitly stated. (Notes are non-normative.)

However, one can try to deduced it from §3.10/2:

An lvalue refers to an object or function.

When dereferencing, the result is an lvalue. A null pointer does not refer to an object, therefore when we use the lvalue we have undefined behavior. The problem is that the previous sentence is never stated, so what does it mean to "use" the lvalue? Just even generate it at all, or to use it in the more formal sense of perform lvalue-to-rvalue conversion?

Regardless, it definitely cannot be converted to an rvalue (§4.1/1):

If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

Here it's definitely undefined behavior.

The ambiguity comes from whether or not it's undefined behavior to deference but not use the value from an invalid pointer (that is, get an lvalue but not convert it to an rvalue). If not, then int *i = 0; *i; &(*i); is well-defined. This is an active issue.

So we have a strict "dereference a null pointer, get undefined behavior" view and a weak "use a dereferenced null pointer, get undefined behavior" view.

Now we consider the question.


Yes, (a) results in undefined behavior. In fact, if this is null then regardless of the contents of the function the result is undefined.

This follows from §5.2.5/3:

If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form (*(E1)).E2;

*(E1) will result in undefined behavior with a strict interpretation, and .E2 converts it to an rvalue, making it undefined behavior for the weak interpretation.

It also follows that it's undefined behavior directly from (§9.3.1/1):

If a nonstatic member function of a class X is called for an object that is not of type X, or of a type derived from X, the behavior is undefined.


With static functions, the strict versus weak interpretation makes the difference. Strictly speaking, it is undefined:

A static member may be referred to using the class member access syntax, in which case the object-expression is evaluated.

That is, it's evaluated just as if it were non-static and we once again dereference a null pointer with (*(E1)).E2.

However, because E1 is not used in a static member-function call, if we use the weak interpretation the call is well-defined. *(E1) results in an lvalue, the static function is resolved, *(E1) is discarded, and the function is called. There is no lvalue-to-rvalue conversion, so there's no undefined behavior.

In C++0x, as of n3126, the ambiguity remains. For now, be safe: use the strict interpretation.

What will happen when I call a member function on a NULL object pointer? [duplicate]

It's undefined behavior, so anything might happen.

A possible result would be that it just prints "fun" since the method doesn't access any member variables of the object it is called on (the memory where the object supposedly lives doesn't need to be accessed, so access violations don't necessarily occur).

Invoking a method from null pointer in the lambda

As per this post, yes, that would result in undefined behaviour.

If you wish to avoid the undefined behaviour, which happens if the code attempts to do something which is not defined by the standard, then yes, you will avoid UB if the code doesn't execute, which can be avoided with a condition.

May I have a real life example where a non-static member function not accessing the object being called via a null pointer causes observable problems? [duplicate]

Simple:

struct Object { void foo(); std::string s; };

void print(Object* o) {
o->foo();
if (o) { std::cout << o->x << "\n"; }
}

Let us say that foo does not access any non-static attribute of Object (ie, x).

The problem is that because formally o->foo() is undefined behavior if o is null, then it is obvious that o is not null. Therefore the check is redundant.

The function is thus optimized:

void print(Object* o) {
o->foo();
std::cout << o->x << "\n";
}

Reversing the order does not change anything:

void print(Object* o) {
if (o) { std::cout << o->x << "\n"; }
o->foo();
}

is still optimized:

void print(Object* o) {
std::cout << o->x << "\n";
o->foo();
}

Sometimes referred to as the Time Travel Clause of Undefined Behavior by some SO members.

For more information, check out Chris Lattner's serie on Undefined Behavior:

  • What Every C Programmar Should Know About Undefined Behavior 1/3
  • What Every C Programmar Should Know About Undefined Behavior 2/3
  • What Every C Programmar Should Know About Undefined Behavior 3/3

Your specific concern is addressed in 2/3.

Whether this actually fails depend on the compiler you use, the optimization passes you specify and the order in which they run.

Do you really want to depend on all that :x ?

Of course, one would argue that's it is pointless to have a function member that does not access any state of the object... so the question itself is of little value in practice (but interesting for its theoretical aspects).

C++ Impossible nullptr call mystery [duplicate]

Even If I try to dereference it on purpose, it DOES succeed.

No, the program has undefined behavior meaning it is still in error even if it doesn't say so explicitly and "seems to be working". This is due to the use of -> for dereferencing in your program.

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.

Is calling a function on a NULL pointer undefined? [duplicate]

I'm looking for the reference that says a->x is equivalent to (*a).x.

Here it is:

[C++11: 5.2.5/2]: For the first option (dot) the first expression shall have complete class type. For the second option (arrow) the first expression shall have pointer to complete class type. The expression E1->E2 is converted to the equivalent form (*(E1)).E2; the remainder of 5.2.5 will address only the first option (dot). In either case, the id-expression shall name a member of the class or of one of its base classes. [ Note: because the name of a class is inserted in its class scope (Clause 9), the name of a class is also considered a nested member of that class. —end note ] [ Note: 3.4.5 describes how names are looked up after the . and -> operators. —end note ]

There is no direct quotation for dereferencing a NULL pointer being UB, unfortunately. You may find more under this question: When does invoking a member function on a null instance result in undefined behavior?



Related Topics



Leave a reply



Submit