Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing static member through invalid pointer: guaranteed to "work"? [duplicate]

Tags:

c++

Setup

Given this user-defined type:

struct T
{
    static int x;
    int y;

    T() : y(38);
};

and the requisite definition placed somewhere useful:

int T::x = 42;

the following is the canonical way to stream the int's value to stdout:

std::cout << T::x;

Control

Meanwhile, the following is (of course) invalid due to an instance of T not existing:

T* ptr = NULL; // same if left uninitialised
std::cout << ptr->y;

Question

Now consider the horrid and evil and bad following code:

T* ptr = NULL;
std::cout << ptr->x; // remember, x is static

Dereferencing ptr is invalid, as stated above. Even though no physical memory dereference takes place here, I believe that it still counts as one, making the above code UB. Or... does it?

14882:2003 5.2.5/3 states explicitly that a->b is converted to (*(a)).b, and that:

The postfix expression before the dot or arrow is evaluated; This evaluation happens even if the result is unnecessary to determine the value of the entire postfix expression, for example if the id-expression denotes a static member.

But it's not clear whether "evaluation" here involves an actual dereference. In fact neither 14882:2003 nor n3035 seem to explicitly say either way whether the pointer-expression has to evaluate to a pointer to a valid instance when dealing with static members.

My question is, just how invalid is this? Is it really specifically prohibited by the standard (even though there's no physical dereference), or is it just a quirk of the language that we can probably get away with? And even if it is prohibited, to what extent might we expect GCC/MSVC/Clang to treat it safely anyway?

My g++ 4.4 appeared to produce code that never attempts to push the [invalid] this pointer onto the stack, with optimisations turned off.

BTW If T were polymorphic then that would not affect this, as static members cannot be virtual.

like image 540
Lightness Races in Orbit Avatar asked Mar 09 '11 16:03

Lightness Races in Orbit


2 Answers

it's not clear whether "evaluation" here involves an actual dereference.

I read "evaluation" here as "the subexpression is evaluated." That would mean that the unary * is evaluated and you perform indirection via a null pointer, yielding undefined behavior.

This issue (accessing a static member via a null pointer) is discussed in another question, When does invoking a member function on a null instance result in undefined behavior? While it discusses member functions specifically, I don't see any reason that data members are any different in this respect. There is some good discussion of the issue there.

There was a defect reported against the C++ Standard that asks "Is call of static member function through null pointer undefined?" (see CWG Defect 315) This defect is closed and its resolution states that it is valid to call a static member function via a null pointer:

p->f() is rewritten as (*p).f() according to 5.2.5 [expr.ref]. *p is not an error when p is null unless the lvalue is converted to an rvalue

However, this resolution is in fact wrong.

It presupposes the concept of an "empty lvalue," which is part of the proposed resolution for another defect, CWG defect 232, which asks the more general question, "Is indirection through a null pointer undefined behavior?"

The resolution to that defect would make certain forms of indirection through a null pointer (like calling a static member function) valid. However, that defect is still open and its resolution has not been adopted into the C++ Standard. Until that defect is closed and its resolution is incorporated into the C++ Standard, indirection via a null pointer (or dereferencing a null pointer, if one prefers that term) always yields undefined behavior.

like image 102
James McNellis Avatar answered Oct 17 '22 03:10

James McNellis


Concerning p->a, where p is a null pointer, and a a static data member: §9.4/2 says "A static member may be referred to using the class member access syntax, in which case the object-expression is evaluated." (The "object-expression" is the expression to the left of the . or the ->.)

like image 32
James Kanze Avatar answered Oct 17 '22 01:10

James Kanze