Is it UB to return a pointer to local variable?

Viewed 202

Yes, I know perfectly well you should not do that. If we have this code:

int *foo() {
    int a = 42;
    return &a;
}

As most C coders know, this is undefined behavior: Using pointer after free()

int *p = foo();
printf("%d\n", *p);

Just so that future readers don't take this as the truth. Everything below until the question was based on a false assumption from me. It's not UB. It's just bad.

As some C coders know, but less than for above, that this also is UB, even though no dereferencing is done: (Was trying to find a good question about this, but did not find anyone)

int *p = foo();
printf("%p\n", p); // Should be printf("%p\n", (void*) p);
                   // Eric P clarified this in his answer, but this missing cast
                   // is not a part of the primary question

And for the same reason, also this is UB, because what happens, is that the pointer become indeterminate, just like an uninitialized variable. A so called dangling pointer.

int *p = foo();
int *q = p;

And somewhat surprising to some, even this is not ok:

free(ptr);
if(ptr == NULL) // Just as bad as if ptr is not initialized

The question

What I do wonder, is if also this single line invokes UB:

int *p = foo();

Or maybe even this?

foo();

In other words, does p become a dangling pointer, or does it get assigned to a dangling pointer?

I don't know if there's any practical use for this, except deeper understanding for the C language. Well one good use case would be to figure out which refactorings that are urgent and which can wait.

1 Answers

C 2018 6.2.4 says “… The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.” 3.19.2 tells us an indeterminate value is “either an unspecified value or a trap representation.” 3.19.3 tells us an unspecified value is a “valid value of the relevant type where this document imposes no requirements on which value is chosen in any instance” (meaning the value can appear to be different each time we use it, even if no apparent changes are made to it).

Thus in:

int *p = foo();
printf("%p\n", (void *) p); // "(void *)" added to pass correct type for %p.

We do not know what value will be printed for p. If the C implementation has no trap representations for pointers, then its indeterminate value cannot be a trap representation, so there is no undefined behavior. However, it is, per 3.19.3, some valid value.

This answer does not speak to other questions in the post, such as whether:

int *p = foo();
int *q = p;

assigns to q some value that is fixed once the assignment is done (thus printing q repeatedly always prints the same value) or assigns to q the notional “indeterminate value” (thus printing q repeatedly would be allowed to print different values). (Either way, it is not undefined behavior, except for the possibility of trap representations.)

Related