-3

This is a related question to the discussion around Example of error caused by UB of incrementing a NULL pointer

Suppose I define this data structure:

union UPtrMem
{
    void* p;
    char ach[sizeof(void*)];
}

UPtrMem u;
u.p = nullptr;
u.p++;      // UB according to standards
u.ach[0]++; // why is this OK then??

p and ach share the same memory, so is merely the act of modifying a memory location (that happens to contain a pointer) UB? I would think it only gets undefined once you try to dereference the pointer.

Community
  • 1
  • 1
Ron Kuper
  • 817
  • 5
  • 14
  • 2
    I don't understand how this question differs from the one you linked to (and the one linked from that one too). When a program contains UB it contains UB. Your program intentionally contains UB at `u.p++` as explained in that question. What else is there to say? – Shoe May 20 '15 at 12:58
  • AFAIK, aliasing a pointer with a `char` array and doing something with the `char` array is not guaranteed to have any reasonable effect when you interpret the memory as a pointer again. –  May 20 '15 at 13:01
  • 1
    You basically took [this question](http://stackoverflow.com/q/29825352/493122) and [this question](http://stackoverflow.com/questions/10271929/union-for-uint32-t-and-uint8-t4-undefined-behavior) and put them in the same question. I'm very tempted to close this question as a duplicate of at least one. Any reason why I shouldn't? – Shoe May 20 '15 at 13:03
  • 1
    @Jefffery: I think the OP left a crucial part of his question implicit: I suspect he actually means to ask "`u.ach[0]++` does the same thing as `u.p++`, so why is one UB and the other not?". –  May 20 '15 at 13:03
  • @Hurkyl `u.ach[0]` is not even a pointer type, and it's never initialized with `nullptr` anywhere. As the question he linked explains, the problem is not with incrementing an integral value, rather with incrementing a null pointer. So I'm not following your point. – Shoe May 20 '15 at 13:05
  • Oh, well. I'm going to close it. If someone thinks it should be reopened, please cast your vote. – Shoe May 20 '15 at 13:06
  • @Jefffrey: Of course `u.ach[0]` is not a pointer type: it's one of the bytes of the memory where `u.p` is stored. –  May 20 '15 at 13:07
  • 1
    **−1** Not the real code, plus apparently designed to trap responders. – Cheers and hth. - Alf May 20 '15 at 13:08
  • @Hurkyl Actually no. It's one element of an array of N characters which have nothing to do with the fact that the union also contains a pointer type. So no, `ach` has nothing to do with pointers. But I see what you are getting at. – Shoe May 20 '15 at 13:10
  • Umm really not meant to be trap, I'd be happy to comment out the line that doesn't compile, just curious and I got my answer so thanks! – Ron Kuper May 20 '15 at 17:38

4 Answers4

10

This is still UB because

it's undefined behavior to read from the member of the union that wasn't most recently written.

(from here). So you have UB, regardless of the value of p. To conclude:

why is this OK then??

It is not.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
  • You would have to **change the code** (not merely the missing semicolon but also the type of pointer) to make it compile. So it is not true that “this” is UB. You might say that *if* the code is changed in such and such a way, then that changed code would compile and exhibit UB, but that's all. That said, with the semicolon added and the pointer changed to `char*`, the code is only formally UB, not problematic, because a pointer is at least one byte (i.e. overlapping the `char`), and because apparently no extant C++ implementation has a nullpointer value that is not the bitpattern all zeroes. – Cheers and hth. - Alf May 20 '15 at 18:13
  • @Cheersandhth.-Alf I interpreted the question as *"Why can't it do the first increment but the second one instead"*. Apparently, that was not entirely off because OP accepted the answer. I explained that he cannot do the second increment either, "regardless of the value of `p`". I realized the missing semicolon, but felt like that was not the point of the question. And formally UB is still UB. – Baum mit Augen May 20 '15 at 19:44
1

The reason why the standard makes incrementing a null pointer undefined is because it is not always the case that a null pointer contains an arithmetically meaningful value like 0. It could contain a specific bit pattern that indicates non-addressable memory to the CPU.

Your example has other problems too.

When you increment an allocated pointer it adds the size of the thing it points to to its value.

So on a 32bit computer and int* will likely advance 4 places (sizeof(int)) when you add 1 to it.

The problem with void* is the compiler has no size information and so can not know how far to increment its value.

In your example you then do this:

u.ach[0]++;

That doesn't increment a pointer at all, it increments whatever char value is contained in the first element of the char array. This, of course, is undefined so, even though it works, you can not rely on it having any specific value.

Galik
  • 47,303
  • 4
  • 80
  • 117
  • This is all correct, but is unrelated to the question. The question is about the undefined-ness of incrementing null pointers, not the validity of arithmetic on void-pointers. – molbdnilo May 20 '15 at 13:02
  • @molbdnilo You're right, should have read the question more clsely. – Galik May 20 '15 at 13:17
1

Your example doesn't contain any UB, because you don't get that far: it's invalid code that just won't compile.

To have the kind of UB you're thinking about, the title's “UB when manipulating nullptr”, you need to have the code executed.

That doesn't happen when it doesn't compile.


Just in case the question is changed after I answer, which isn't uncommon with these kinds of apparently designed-to-trap-the-responder questions, this is the code presented as I'm writing this:

union UPtrMem
{
    void* p;
    char ach[sizeof(void*)];
}

UPtrMem u;
u.p = nullptr;
u.p++;      // UB according to standards
u.ach[0]++; // why is this OK then??

Incrementing a void* is just invalid, not a supported operation, and won't compile.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • I just wanna add that it actually compiles on gcc due to an extension if the OP used it (if you add the missing semicolon after the union). Arithmetic on `void *` basically gets treated like `char *`. It would clearly warn about UB though: [example](http://coliru.stacked-crooked.com/a/9d6707b1c26b06f3) – AliciaBytes May 20 '15 at 13:13
  • @RaphaelMiedl: The `-pedantic-errors` option fixes that. ;-) – Cheers and hth. - Alf May 20 '15 at 13:15
  • I know it doesn't compile. I commented out the u.p++; to see what happens (also) just to confirm. – Ron Kuper May 20 '15 at 13:18
  • Sincere apologies, not meaning to trap anyone, I won't change a thing. – Ron Kuper May 20 '15 at 17:45
-3

Seems to me u.p++; isn't even valid because void has no size so - nothing to increment. But u.ach[0]++; is valid because your incrementing a char.

edit yes it takes up space in the structure... but what it points to has no size... what would it increment by?

mark
  • 5,269
  • 2
  • 21
  • 34
  • Not my downvote, but 1) we're talking about `void *` here, which has a size, and 2) your answer, as well as mine, are moot in face of Baum's answer -- `u.ach[0]` is undefined behavior in this case. – Frédéric Hamidi May 20 '15 at 12:57
  • 1
    incrementing a pointer to char would advance by one. incrementing a pointer to a structure would advance by the size of the structure. how would incrementing a void work? – mark May 20 '15 at 12:57
  • pointer arithmetic on void pointers is completely illegal since "void has no size" like you said yourself. gcc allows it as an extension and handles it the same way like `char *` though, see: [Pointer arithmetic for void pointer in C](http://stackoverflow.com/q/3523145). Also incrementing a `nullptr` is UB, see: [Is incrementing a null pointer well-defined?](https://stackoverflow.com/q/29825352). – AliciaBytes May 20 '15 at 12:58
  • FYI: doesn't even compile in MSVC++2013... – mark May 20 '15 at 13:06