19

I am trying to decipher a note that led to a change between C99 and C11. The change proposed in that note ended up in C11's 6.2.4:8, namely:

A non-lvalue expression with structure or union type, where the structure or union contains a member with array type (including, recursively, members of all contained structures and unions) refers to an object with automatic storage duration and temporary lifetime. Its lifetime begins when the expression is evaluated and its initial value is the value of the expression. Its lifetime ends when the evaluation of the containing full expression or full declarator ends. Any attempt to modify an object with temporary lifetime results in undefined behavior.

I understand why the change was needed (some discussion can be found here. Note that the discussion goes back to before C11). However, what I don't understand is a side remark that Clark Nelson made in writing his note:

Please note that this approach additionally declares an example like this, which was conforming under C99, to be non-conforming:

struct X { int a[5]; } f();
int *p = f().a;
printf("%p\n", p);

I understand why this example is non-conforming under C11. What I specifically fail to understand is how it is conforming under C99. And, if it is defined under C99, what is it supposed to do then, definedly print the value of a dangling pointer?

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • 1
    I fail to understand how that is NON-conforming under either C99 or C11, as it does not attempt to modify the object with temporary lifetime, or access it after the lifetime ends. The exact value of the pointer it prints is obviously unknown, but it should print some pointer value. – Chris Dodd Oct 01 '12 at 17:38
  • 1
    @ChrisDodd The reason it is undefined in C11 is that it contravenes 6.2.4:2, specifically “The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.”. It is morally undefined behavior to access indeterminate contents (it is listed as such in J.2: “The value of a pointer to an object whose lifetime has ended is used (6.2.4)”, although the actual normative body is a little more ambiguous). C99 is very similar in that respect. The only difference I see is that p does not **become** indeterminate because it's never determinate. – Pascal Cuoq Oct 01 '12 at 17:48

1 Answers1

8

My understanding is that in C99, the finest grain of lifetime for an object is the block. Thus, while 6.5.2.2 (and some other § mentioned in the note you refer to) specifically says that you can't access the returned value after the next sequence point, technically its address is not indeterminate until after you have left the enclosing block (the reason why you should have some storage reserved for an inaccessible object is left as an exercise for the reader, though). Thus, something like

struct X { int a[5]; } f();
int *p;
{ p = f().a; }
printf("%p\n", p);

is undefined in C99 as well as in C11. In C11, the notion of "temporary lifetime", that does not exist in C99, allows to consider that the pointer becomes indeterminate as soon as the full expression ends.

Virgile
  • 9,724
  • 18
  • 42
  • "the finest grain of lifetime for an object is the block" Agreed, for objects with automatic storage duration (C99 6.2.4:5) – Pascal Cuoq Oct 02 '12 at 08:48