2

Consider the following piece of C++ code:

int main() {
    int* ptr; // Deliberately uninitialized
    (void) *ptr;     // Dereference, do not use result
}

Does this code result in undefined behavior? I assume that the answer is "yes" even though the value of *ptr is not actually used anywhere. Is there a particular part of the spec that guarantees this?

(I apologize if this is a duplicate, but I can't seem to find anything else on the site that specifically asks this question.)

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • At least undefined behavior is free from the as-if rule. :) – wally Feb 29 '16 at 22:52
  • 2
    Why do you care? Why would you dererence a pointer without using the value? – Barmar Feb 29 '16 at 22:53
  • Doing this under Windows could cause the `0xC00000005 -- Read Access Violation` to kick in. – PaulMcKenzie Feb 29 '16 at 22:55
  • 5
    The C++ equivalent of the [If a tree falls in a forest](https://en.wikipedia.org/wiki/If_a_tree_falls_in_a_forest) philosophical conundrum. – Ami Tavory Feb 29 '16 at 22:59
  • 3
    I'd be pretty disappointed if the comitee spent time answering the question "what aould happens if an invalid pointer is dereferenced but the result is discarded".. as the saying goes "this is why we can't have nice things" – David Haim Feb 29 '16 at 23:09
  • @DavidHaim: Both the C and C++ committees have spent discussing essentially that question, but for past-the-end-of-array pointer, not invalid pointer. – Cheers and hth. - Alf Feb 29 '16 at 23:11
  • @Cheersandhth.-Alf plus the condition "and the result is discarded"?! – David Haim Feb 29 '16 at 23:12
  • @DavidHaim: Yes, that was the whole point. Namely expressions of the form `&*p`, which occur e.g. when saying `&a[n]` instead of saying `a+n`. Which ideally should be equivalent, but the latter is clearly just computing a pointer value, in the well-defined range of pointer values. :) – Cheers and hth. - Alf Feb 29 '16 at 23:15
  • @templatetypedef: There was a cleanup of the UB-for-pointer issues in C++11. In C++03 there was a (non-normative) note explaining that the standard elsewhere somewhere specified UB, but no such language elsewhere, except as vaguely implied by the exception for `typeid` expressions. As I recall the C++11 cleanup was far from perfect, not clear-cut. So I suspect there is no clear-cut answer yet. Maybe with C++20. ;-) – Cheers and hth. - Alf Feb 29 '16 at 23:25
  • 1
    Note the discussion in (http://stackoverflow.com/questions/4285895/where-exactly-does-c-standard-say-dereferencing-an-uninitialized-pointer-is-un), brought up by @MarinosK – Cheers and hth. - Alf Feb 29 '16 at 23:40
  • It appears that the `C++11` Standard does state that dereferencing an uninitialized pointer is undefined behavior (See my answer for the quote). – Galik Mar 01 '16 at 00:05
  • @Galik, yes, but in what sense is `(void)*ptr` "dereferencing" `ptr`? It's a discarded-value expression which explicitly does not cause lvalue-to-rvalue conversion (since `ptr` is not declared to be `int volatile*`) – rici Mar 01 '16 at 00:16
  • @rici The standard says that all operations on an uninitialized *pointer* (except overwriting the value) are undefined. I am not sure why the lack of *lvalue to rvalue* conversion for the pointed to object is important? Is that a guarantee that the `CPU` will not attempt to read the value in from memory? – Galik Mar 01 '16 at 00:27
  • @Galik: It is precisely the lvalue-to-rvalue conversion which reads the value pointed at. Does the fact that an lvalue-to-rvalue conversion is not done *guarantee* that the CPU will not attempt to read the value? That I don't know for sure. But I suspect it is, because there should be some meaning to the fact that the standard explicitly states that lvalue-to-rvalue conversion is not performed on discarded values; otherwise, why bother even saying that? – rici Mar 01 '16 at 00:33
  • @Galik: Also, it seems reasonable that `int *x; int *y; y = &*x;` *is* UB, because it is quite possible for pointer types to have trap values (indeed, this may even be the case on real hardware). But the discarded value doesn't even need to read the pointer value, never mind what it points at, since the value is discarded. – rici Mar 01 '16 at 00:46

4 Answers4

8

Simply dereferencing a pointer that has not been allocated or is marked as read-only can cause a hardware exception in the CPUs memory management unit. So even if you don't use whatever garbage value would be returned by dereferencing a pointer that contains a random value, there is no guarantee the CPU would return from such an event.

However, according to the ISO C++11 Standard a pointer that is declared uninitialized must have a singular value. So the value of an uninitialized pointer is not undefined as in it is not garbage.

However the Standard states that most operations on such a singular value pointer are undefined with the exception of overwriting the singular value with a non singular value:

24.2.1 In general [ iterator.requirements.general ]

5 [ Example: After the declaration of an uninitialized pointer x (as with int* x;), x must always be assumed to have a singular value of a pointer. — end example ] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation. [ Note: This guarantee is not offered for default initialization, although the distinction only matters for types with trivial default constructors such as pointers or aggregates holding pointers. — end note ] In these cases the singular value is overwritten the same way as any other value. Dereferenceable values are always non-singular.

Galik
  • 47,303
  • 4
  • 80
  • 117
1

You're reading the value of an uninitialized variable (the pointer). That is undefined behavior.

Interestingly, because *ptr is a discarded value expression, it isn't going to undergo lvalue to rvalue conversion. So if *ptr was uninitialized, it would be safe.

Weak to Enuma Elish
  • 4,622
  • 3
  • 24
  • 36
-1

According to the standard, dereferencing a non-initialised pointer is undefined behaviour. However, in real life this is rarely the case: compilers are clever enough to remove code like this since it's not actually used anywhere.. So even if dereferencing the pointer is undefined behaviour, if you don't use the result the chances are that you don't actually dereference it. I tested your code with clang++ and NO CODE has been generated.

from the standard (4.1):

An lvalue (3.10) of a non-function, non-array type T can be converted to an rvalue. If T is an incomplete type, a program that necessitates this conversion is ill-formed. 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. If T is a non-class type, the type of the rvalue is the cv-unqualified version of T. Otherwise, the type of the rvalue is T.

Marinos K
  • 1,779
  • 16
  • 39
  • 2
    Please quote any relevant reference. – Cheers and hth. - Alf Feb 29 '16 at 23:10
  • if something is not explicitly defined in the standard it is by definition undefined, and there is no defined behaviour for dereferencing an initialised variable.. this is explicit in 4.1 too: http://stackoverflow.com/questions/4285895/where-exactly-does-c-standard-say-dereferencing-an-uninitialized-pointer-is-un – Marinos K Feb 29 '16 at 23:23
  • Hm, I'm not sure of the validity of that argument. But it would still be nice if you quoted the relevant parts of the standard that support the argument. Plus, of course, the argument itself. – Cheers and hth. - Alf Feb 29 '16 at 23:44
  • I've quoted the relevant part above – Marinos K Mar 01 '16 at 05:31
-2

It is not undefined behavior according to the Standard.

Here is the official discussion and explanation from the open-std review page.

We agreed that the approach in the standard seems okay: p = 0; *p; is not inherently an error. An lvalue-to-rvalue conversion would give it undefined behavior.

Additionally in the standard [Section 4.10.2], it says:

A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type “pointer to cv void”. The result of converting a non-null pointer value of a pointer to object type to a “pointer to cv void” represents the address of the same byte in memory as the original pointer value. The null pointer value is converted to the null pointer value of the destination type.

callyalater
  • 3,102
  • 8
  • 20
  • 27
  • I think this is a good answer but I am **not** upvoting, because the assertion at top is unfounded. It is a consensus belief, and it is what further revisions of the standard will aim at. I don't think its a fact (yet). – Cheers and hth. - Alf Feb 29 '16 at 23:29
  • 1
    The asker doesn't have a null pointer, but an uninitialized pointer. It looks like this caveat applies only to null pointers, but not random memory addresses. – Weak to Enuma Elish Feb 29 '16 at 23:29
  • The pointer is undergoing a conversion from an `int*` to a `void` in the dereference which was the part I was referring to. – callyalater Feb 29 '16 at 23:33
  • 2
    **−1** Re "This is almost identical to the example given.", no, that's associative thinking. Conversion to `void`, a *discarded expression*, is very different from conversion to `void*`. – Cheers and hth. - Alf Feb 29 '16 at 23:35
  • @Cheersandhth.-Alf, Understood. I will change that. – callyalater Feb 29 '16 at 23:37