0

In this sample code, can the pointer really be invalid after the while loop, and should one really take this into account while writing code? Or is the C standard misinterpreted and/or defective?

#include <stdio.h>

int main(int argc, char **argv) {
    (void)argc;
    (void)argv;

    int *pointer;
    int object[1];

    pointer = object;
    printf("pointer -before: %p\n", (void*)pointer);

    do {
        int other_object[1];

        printf("a pointer \"just past\" other_object can look like: %p\n", (void*)(&other_object+1));
        printf("address of other_object: %p\n", (void*)&other_object);
    } while (0);
    puts("the lifetime of other_object has ended");
    printf("pointer -after: %p\n", (void*)pointer);
}

Possible output ( a run on my machine ):

pointer -before: 0x7fff5f744ae4
a pointer "just past" other_object can look like: 0x7fff5f744ae4
address of other_object: 0x7fff5f744ae0
the lifetime of other_object has ended
pointer -after: 0x7fff5f744ae4

It seems to be an indeterminate pointer according to an accepted SO answer: Array resizing and realloc function

This issue is also referred to in the following article, with more sample code producing unexpected output due to undefined behaviour:

http://trust-in-soft.com/dangling-pointer-indeterminate/

Both quote this sentence from ISO: "The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime."


EDIT: Changed the source code a bit based on comments about int* vs void*


EDIT: Changed the source code to contain arrays.

Community
  • 1
  • 1
Gábor Buella
  • 1,840
  • 14
  • 22
  • You invoked *undefined behavior* for passing data having wrong type to `printf()`, so it may be. Cast your pointer to `void*` before passing it for printing via `%p`. – MikeCAT Mar 06 '16 at 00:25
  • 5
    The code you posted does not modify (or, in fact, use at all) the `pointer` variable in the loop - unlike the article you linked. In your case, there is no reason why `pointer` would become _indeterminate_. Maybe you should explain why you _think_ that could happen, to begin with. – dxiv Mar 06 '16 at 00:47
  • "why you think that could happen" I added sample output, so it easier to see. 'pointer' can easily point just past other_object, which reaches the end of it's lifetime. – Gábor Buella Mar 06 '16 at 00:50
  • 2
    You can only compare pointers to elements of the _same_ array, in which case it makes sense to talk about _one past the end_. However, `pointer` in your example points to an individual integer. It is irrelevant if the address in hex relates in whatever way to the address of another local variable. The two are not part of the same array, cannot be compared, and in fact you try to subtract them you invoke undefined behavior. Lookup for example the 2nd paragraph at [ptrdiff_t](http://en.cppreference.com/w/c/types/ptrdiff_t). – dxiv Mar 06 '16 at 00:53
  • "when the object it points to (or just past)" <- does not mention arrays. But I could make other_object an array as well... – Gábor Buella Mar 06 '16 at 00:57
  • 2
    Your `pointer` points _to_ an object, not _past_ it. The pointer doesn't change, and the lifetime of the object pointed to doesn't end until the end of `main`. Sorry, I still can't quite figure out what the reasoning behind your question actually is. – dxiv Mar 06 '16 at 01:04
  • The pointer need not be what you call "just past a object". http://melpon.org/wandbox/permlink/KSluUnJKkpLRW2VD – MikeCAT Mar 06 '16 at 01:13
  • @MikeCAT Yes, it need not be. That is the point, we do not know if pointer points just past `other_object`, or does not. And I don't see anything stopping it from doing that. At least not in the text from ISO. – Gábor Buella Mar 06 '16 at 01:26
  • 2
    Rather than looking for ways to interpret the standard that would result in crippling the language, perhaps you should take your observation that your interpretation would cripple the language as evidence that that interpretation is *wrong*. I might have thought that was what you were doing in your question, but in comments you seem very reluctant to discard your misinterpretation. – John Bollinger Mar 06 '16 at 01:41
  • 1
    A pointer points to or just past an object, or it is a null pointer. It might happen to compare equal to a pointer which points to or just past a different object, but those two pointers are not otherwise related. The object which a pointer references is fixed, even if that is not explicit in the representation of the pointer. So although there may be no way to tell at runtime whether a given pointer is one past object A or at the beginning of object B, *only one of those statements is true*. And the pointer is invalidated only by the end of lifetime of the object it refers to. – rici Mar 06 '16 at 05:46
  • ... On a given system, a pointer might actually be stored as a pointer to the beginning of its object combined with an offset into (or just past) the object. There are actually such implementations, used to detect buffer overruns, and the standard clearly allows them. On such a system, two pointers with the same effective address would be distinguishable. The standard does not require the implementation to do so, but it does require the programmer to know. – rici Mar 06 '16 at 05:50
  • the key factor is the pointer becomes invalid when it 'reaches the end of its' life time. I.E. when it goes `out of scope`. The `pointer`, for this case, goes `out of scope` when the main() function ends – user3629249 Mar 07 '16 at 00:23

3 Answers3

6

You are parsing this sentence wrong:

The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime

A pointer is allowed to point to an object and, in the case of an array, it is allowed to point to an element one past the last element of that array.

This means: if a pointer points to an object, or, if it points to an element one past the last element of an array, then it becomes indeterminate when that object or that array reaches the end of its lifetime.

It doesn't mean that the value of a pointer becomes indeterminate when an object that just happens to come immediately after the object you're pointing to reaches the end of its lifetime, or if the pointed-to object itself comes in memory after a different object which has reached the end of its lifetime, which is what you seem to be understanding it to mean. In your example, the fact that other_object lives at a memory address immediately before the object you're pointing to, and then reaches the end of its lifetime, is completely irrelevant, because the object you're actually pointing to - namely, object - is still alive, and at no point does your pointer ever point to anything else but this still-alive object.

Short answer: you are reading things into the words "just past" that are not there. This has meaning only in terms of a pointer to an array object being incremented to an element one past the end. It has nothing at all to do with any other completely unrelated objects which may just happen to immediately follow the pointed-to object in memory.

Crowman
  • 25,242
  • 5
  • 48
  • 56
  • "lives at a memory address immediately after the object you're pointing to" part is wrong, it is "immediately before". Actually immediately before. Thus, pointer points immediately after `other_object` . Also, where do you guys get this array thing from? ISO mentions an object where I looked. Not specifically only arrays ( btw `other_object` can be replaced by an array as well – Gábor Buella Mar 06 '16 at 01:08
  • 1
    @BuellaGábor: Either way, your pointer points to `object`, and so the lifetime of `other_object` has nothing to do with anything. The "array thing" was explained in the comments - it makes no sense to talk about "one past the end" for anything other than an array. – Crowman Mar 06 '16 at 01:12
  • 2
    @BuellaGábor `it is "immediately before". Actually immediately before.` No. That's compiler and architecture dependent. The standard says nothing about how addresses of different objects (which are not elements of the same array) may or may not relate to each other. In fact, as I said already in another comment, the standard makes it UB to even try to compare addresses of unrelated objects. – dxiv Mar 06 '16 at 01:13
  • @PaulGriffiths An object can be considered as an element of array having one element in pointer aritimetic: quote from [N1256](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) 6.5.6 Additive operators: "7 For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type." – MikeCAT Mar 06 '16 at 01:15
2

You invoked two undefined behaviors by

  • Passing data having wrong type to printf(): %p calls for void*, but you passed int*.
  • Subtracting two pointers which are not pointing at elements of the same array object.

so, anything can happen and the pointer may be indeterminate.

If you hadn't invoked undefined behavior, it wouldn't become indeterminate because pointer isn't updated since the address of object, which is still alive after the do statement, is assigned to it.

MikeCAT
  • 73,922
  • 11
  • 45
  • 70
2

You ask,

In this sample code, can the pointer really be invalid after the while loop, and should one really take this into account while writing code? Or is the C standard misinterpreted and/or defective?

, apparently based on this text taken (slightly out of context) from the standard:

"The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime."

But the standard is not there talking about a pointer that incidentally is equal to a pointer to just past the end of an unrelated object. Rather, that text is referring to the standard's explicit provision for a pointer to one position past the end an array to be valid. It is when the basis for a pointer's validity rests on it pointing just past the end of an object that the pointer becomes indeterminate when that object reaches the end of its lifetime. Generally, that situation arises when the pointer in question is derived by pointer arithmetic from a pointer to (or into) the object.

So no, in your code, the value of pointer does not become indeterminate when other_object reaches the end of its lifetime, notwithstanding its value relative to a pointer to other_object during its lifetime.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • where is this arrays only thing mentioned in the standard? – Gábor Buella Mar 06 '16 at 01:18
  • "that situation arises when the pointer in question is derived by pointer arithmetic from a pointer to (or into) the object." Where is this mentioned in the standard relating to the sentence already mentioned? – Gábor Buella Mar 06 '16 at 01:20
  • @BuellaGábor: C11 6.5.6.9: "When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object". – Crowman Mar 06 '16 at 01:22
  • @PaulGriffiths Look at the current version of the sample code: it does not subtract pointers. – Gábor Buella Mar 06 '16 at 01:23
  • 1
    @BuellaGábor: Then read about addition one paragraph previously, in 6.5.6.8: "If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; *otherwise, the behavior is underfined*." – Crowman Mar 06 '16 at 01:26
  • 2
    @BuellaGábor The standard guarantees that two different objects (both alive in a given scope) have different addresses. The standard does **not** guarantee that a pointer "_one past_" some object has a value different from any other address of a different object. It can, and in your handcrafted example it does. But that's an artifact of the compiler, toolset etc you are using - and it does **not** make the second pointer count as a "_one past_" pointer in the eyes of the standard - simply because the standard forbids ever comparing addresses of unrelated objects. – dxiv Mar 06 '16 at 01:31
  • @BuellaGábor, pointer arithmetic is defined in terms of pointers to arrays. Pointers to other objects are brought in by "For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one." – John Bollinger Mar 06 '16 at 01:32
  • So it is getting weirder: "Two values (other than NaNs) with the same object representation compare equal" So, two pointers can have the same value, the same representation, thus compare equal, and not point to the same thing? The standard forbids comparing them, yet it says they compare equal? – Gábor Buella Mar 06 '16 at 01:52
  • @BuellaGábor Not sure who you are replying to, since you don't use `@` in your comments. That said, you are misquoting something, and should provide the full context. Not all values with the "_same object representation_" can be compared, they must be _comparable_ to begin with - and pointers to unrelated objects are **not**. Anyway, I believe that your question has been fully answered multiple times, and further comments are becoming pointless at this point. – dxiv Mar 06 '16 at 02:01
  • @dxiv, actually, in this case the two pointers are comparable *for equality*, because C2011 6.5.9/6 says "Two pointers compare equal if and only if [...] or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space." But this is irrelevant to the main question. It is as I answered -- the referenced text about pointers becoming indeterminate is coupled with the reason for their validity in the first place. – John Bollinger Mar 06 '16 at 02:08
  • @JohnBollinger As I interpret it, comparison does not have much to do with this. That is an issue when e.g. when the source code contains `a == b` <- here the concept of comparison is used, the compiler needs to make a decision about what code to generate, and it would make sense to talk about well defined, or undefined behaviour, etc. But in the sample code there is no such thing. The pointer points, and there is an object, that's all. The phrase "when the object it points to (or just past) reaches" does not say anything about being coupled with anything else. – Gábor Buella Mar 06 '16 at 02:45
  • @BuellaGábor, I agree that comparisons don't have much to do with it, and in fact I did not raise the topic in my answer, with which these particular comments are associated. That they don't have much to do with it is in fact the point of the other answerers and commenters who did raise the issue: it is not valid to compare pointer values as you did to evaluate whether one pointer "points one past the end of an array" for the purposes of interpreting the detail of the standard that you asked about. – John Bollinger Mar 06 '16 at 03:00
  • @JohnBollinger Well, I see, we still misunderstand each other. So please, just think about the code without the `printf` lines. There is no printing, there is noone comparing. The pointer is there, and it does point, without being peeked at. The compiler does not guarantee it points to somewhere specific in relation to the other object, that's why of course it is not required to generate meaningful code doing such comparison. It might for example point to just past `other_object`. Even if i don't print it, ISO does not say anything about `printf` affecting it. – Gábor Buella Mar 06 '16 at 03:21
  • @JohnBollinger You are technically right, but with emphasis on `happens to`. To phrase it within the limited rigor allowed by a short comment, I maintain that going strictly by the C standard, it is impossible for the compiler to determine _at compile time_ whether a _one past_ pointer may `happen to` match the address of another object _at runtime_. Because of that, any incidental match between unrelated pointers values at runtime is inconsequential with respect to the _one past_ rule, and therefore the OP question is moot. – dxiv Mar 06 '16 at 04:30
  • @BuellaGábor, I do not misunderstand you, but indeed you do continue to misunderstand me, and apparently everyone else answering and commenting to you. We are all trying to tell you that in the context of the standard and for the purposes of the text you cited from it, pointing "just past" an object has a meaning that is not directly related to the integer representations of pointer values. That the English involved affords other interpretations, such as yours, does not make any of those other interpretations correct. Your interpretation is wrong. – John Bollinger Mar 06 '16 at 18:34
  • @JohnBollinger And I say your interpretation is wrong. Is there a way to decide? I don't see a definition of "just past" anywhere. Indeed, the standard only uses this phrase, and does not give a definition, neither does anyone here. – Gábor Buella Mar 06 '16 at 19:44
  • 1
    @BuellaGábor, yes, there is a way to decide. Your interpretation is wholly unworkable, and therefore implausible. Moreover, in addition to myself, you have at least four other people all much higher-reputed than you all telling you that your interpretation is wrong. If you want a *formal* mechanism, however, then you could submit a request for interpretation to the standards committee. – John Bollinger Mar 06 '16 at 19:50