C++ Standard: Dereferencing Null Pointer to Get a Reference

C++ standard: dereferencing NULL pointer to get a reference?

Dereferencing a NULL pointer is undefined behavior.

In fact the standard calls this exact situation out in a note (8.3.2/4 "References"):

Note: in particular, a null reference cannot exist in a well-defined program, because the only
way to create such a reference would be to bind it to the “object” obtained by dereferencing a null pointer, which causes undefined behavior.


As an aside: The one time I'm aware of that a NULL pointer can be "dereferenced" in a well-defined way is as the operand to the sizeof operator, because the operand to sizeof isn't actually evaluated (so the dereference never actually occurs).

What exactly is meant by de-referencing a NULL pointer?

A NULL pointer points to memory that doesn't exist. This may be address 0x00000000 or any other implementation-defined value (as long as it can never be a real address). Dereferencing it means trying to access whatever is pointed to by the pointer. The * operator is the dereferencing operator:

int a, b, c; // some integers
int *pi; // a pointer to an integer

a = 5;
pi = &a; // pi points to a
b = *pi; // b is now 5
pi = NULL;
c = *pi; // this is a NULL pointer dereference

This is exactly the same thing as a NullReferenceException in C#, except that pointers in C can point to any data object, even elements inside an array.

Assigning a reference by dereferencing a NULL pointer

Dereferencing a null pointer is Undefined Behavior.

An Undefined Behavior means anything can happen, So it is not possible to define a behavior for this.

Admittedly, I am going to add this C++ standard quote for the nth time, but seems it needs to be.

Regarding Undefined Behavior,

C++ Standard section 1.3.24 states:

Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

NOTE:

Also, just to bring it to your notice:

Using a returned reference or pointer to a local variable inside a function is also an Undefined Behavior. You should be allocating the pointer on freestore(heap) using new and then returning a reference/pointer to it.

EDIT:

As @James McNellis, appropriately points out in the comments,

If the returned pointer or reference is not used, the behavior is well defined.

What does C6011 dereferencing null pointer mean in my program?

First of all, note that the warning is generated by your compiler (or static analyzer, or linter), not by your debugger, as you initially wrote.

The warning is telling you that your program possibly might dereference a null pointer. The reason for this warning is that you perform a malloc() and then use the result (the pointer) without checking for NULL values. In this specific code example, malloc() will most likely just return the requested block of memory. On any desktop computer or laptop, there's generally no reason why it would fail to allocate 12 bytes. That's why your application just runs fine and exits successfully. However, if this would be part of a larger application and/or run on a memory-limited system such as an embedded system, malloc() could fail and return NULL. Note that malloc() does not only fail if there is not enough memory available, it could also fail if there is no large enough consecutive block of memory available, due to fragmentation.

According to the C standard, dereferencing a NULL pointer is undefined behavior, meaning that anything could happen. On modern computers it would likely get your application killed (which could lead to data loss or corruption, depending on what the application does). On older computers or embedded systems the problem might be undetected and your application would read from or (worse) write to the address NULL (which is most likely 0, but even that isn't guaranteed by the C standard). This could lead to data corruption, crashes or other unexpected behavior at an arbitrary time after this happened.

Note that the compiler/analyzer/linter doesn't know anything about your application or the platform you will be running it on, and it doesn't make any assumptions about it. It just warns you about this possible problem. It's up to you to determine if this specific warning is relevant for your situation and how to deal with it.

Generally speaking, there are three things you can do about it:

  1. If you know for sure that malloc() would never fail (for example, in such a toy example that you would only run on a modern computer with gigabytes of memory) or if you don't care about the results (because the application will be killed by your OS and you don't mind), then there's no need for this warning. Just disable it in your compiler, or ignore the warning message.

  2. If you don't expect malloc() to fail, but do want to be informed when it happens, the quick-and-dirty solution is to add assert(v != NULL); after the malloc. Note that this will also exit your application when it happens, but in a slightly more controlled way, and you'll get an error message stating where the problem occurred. I would recommend this for simple hobby projects, where you do not want to spend much time on error handling and corner cases but just want to have some fun programming :-)

  3. When there is a realistic change that malloc() would fail and you want a well-defined behavior of your application, you should definitely add code to handle that situation (check for NULL values). If this is the case, you would generally have to do more than just add an if-statement. You would have to think about how the application can continue to work or gracefully shutdown without requiring more memory allocations. And on an embedded system, you would also have to think about things such as memory fragmentation.

The easiest fix for the example code in question is add the NULL-check. This would make the warning go away, and (assuming malloc() would not fail) your program would run still the same.

int main(void) {
uint32_t *v = malloc(3 * sizeof(uint32_t));
if (v != NULL) {
v[0] = 12;
v[1] = 59;
v[2] = 83;
twice_three(v);
free(v);
}
return 0;
}

What are the rules for a valid dereferencing of a null pointer?

What are the rules for a valid dereferencing of a null pointer [in C++]?

C++ standard is actually somewhat non-specific about whether indirecting through a null pointer is valid by itself or not. It is not disallowed explicitly. The standard used to use "dereferencing the null pointer" as an example of undefined behaviour, but this example has since been removed.

There is an active core language issue CWG-232 titled "Is indirection through a null pointer undefined behavior?" where this is discussed. It has a proposed change of wording to explicitly allow indirection through a null pointer, and even to allow "empty" references in the language. The issue was created 20 years ago, has last been updated 15 years ago, when the proposed wording was found insufficient.


Here are a few examples:

X* ptr = nullptr;

*ptr;

Above, the result of the indirection is discarded. This is a case where standard is not explicit about its validity one way or another. The proposed wording would have allowed this explicitly. This is also a fairly pointless operation.

X& x = *ptr;
X* ptr2 = &x; // ptr2 == nullptr?

Above, the result of indirection through null is bound to an lvalue. This is explicitly undefined behaviour now, but the proposed wording would have allowed this.

ptr->member_function();

Above, the result of indirection goes through lvalue-to-rvalue conversion. This has undefined behaviour regardless of what the function does, and would remain undefined in the proposed resolution of CWG-232. Same applies to all of your examples.

One consequence of this is that return this == nullptr; can be optimised to return false; because this can never be null in a well defined program.

dereferencing the null pointer

The answer to this question is: it depends which language standard you are following :-).

In C90 and C++, this is not valid because you perform indirection on the null pointer (by doing *p), and doing so results in undefined behavior.

However, in C99, this is valid, well-formed, and well-defined. In C99, if the operand of the unary-& was obtained as the result of applying the unary-* or by performing subscripting ([]), then neither the & nor the * or [] is applied. For example:

int* p = 0;
int* q = &*p; // In C99, this is equivalent to int* q = p;

Likewise,

int* p = 0;
int* q = &p[0]; // In C99, this is equivalent to int* q = p + 0;

From C99 §6.5.3.2/3:

If the operand [of the unary & operator] is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue.

Similarly, if the operand is the result of a [] operator, neither the & operator nor the unary * that is implied by the [] is evaluated and the result is as if the & operator were removed and the [] operator were changed to a + operator.

(and its footnote, #84):

Thus, &*E is equivalent to E (even if E is a null pointer)

Dereferencing a pointer to 0 in C

C does not prohibit dereferencing the null pointer, it merely makes it undefined behavior.

If your environment is such that you're able to dereference a pointer containing the address 0x0, then you should be able to do so. The C language standard says nothing about what will happen when you do so. (In most environments, the result will be a program crash.)

A concrete example (if I'm remembering this correctly): On the 68k-based Sun 3 computers, dereferencing a null pointer did not cause a trap; instead, the OS stored a zero value at memory address zero, and dereferencing a null pointer (which pointed to address zero) would yield that zero value. That meant, for example, that a C program could treat a null pointer as if it were a valid pointer to an empty string. Some software, intentionally or not, depended on this behavior. This required a great deal of cleanup when porting software to the SPARC-based Sun 4, which trapped on null pointer dereferences. (I distinctly remember reading about this, but I was unable to find a reference; I'll update this if I can find it.)

Note that the null pointer is not necessarily address zero. More precisely, the representation of a null may or may not be all-bits-zero. It very commonly is, but it's not guaranteed. (If it's not, then the integer-to-pointer conversion of (void*)0 is non-trivial.)

Section 5 of the comp.lang.c FAQ discusses null pointers.

Is dereferencing a NULL pointer considered unspecified or undefined behaviour?

The examples are associated with the wrong things. Regardless of what version of the C++ standard you assume (i.e. nothing has changed within the standards, in this regard).

Dereferencing a NULL pointer gives undefined behaviour. The standard does not define any constraint on what happens as a result.

The order of globals initialisation is an example of unspecified behaviour (the standard guarantees that all globals will be initialised [that's a constraint on how globals are initialised] but the order is not specified).



Related Topics



Leave a reply



Submit