9

In C11 the term temporary lifetime was defined:

C11 6.2.4p8: 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. 36) 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 am wondering why this only applies to rvalues of structure or union type which have a member of type array. What is so special about arrays?

struct x { int xx; };
struct y { int yy[1]; };

(struct x)   { 42 };    // *not* a temporary object
(struct y) { { 42 } };  // a temporary object

Why should the first object not be a temporary one while the second is?

Max Maier
  • 985
  • 6
  • 16
  • Does [this question](https://stackoverflow.com/q/12676411/119527) or this rule help your understanding? [EXP35-C. Do not modify objects with temporary lifetime](https://wiki.sei.cmu.edu/confluence/display/c/EXP35-C.+Do+not+modify+objects+with+temporary+lifetime) – Jonathon Reinhart Apr 28 '19 at 06:21
  • That's where I came from :) but I'm still puzzled why arrays have such an important role. Why would I want to distinguish between structures not containing arrays and structures which contain arrays? – Max Maier Apr 28 '19 at 06:27
  • 1
    @MaxMaier you may be mixing things a bit. You generally see `struct y { /* other sutff */ int yy[1]; };` where the array of length one is a bit of a hack that allows `yy` to be referenced as a pointer [C11 Standard - 6.3.2.1(p3)](http://port70.net/~nsz/c/c11/n1570.html#6.3.2.1p3) but have storage of `char`. You see it used in `malloc` and other `stuct` implementations where the address of the last element in the struct is used to reference the block of memory that follows the struct (the struct provides the metadata for the block of memory, e.g. size, can_be_divided, etc..) – David C. Rankin Apr 28 '19 at 07:01
  • 1
    To see that concept in action, see the [awkwardly written, but good basic malloc introduction](https://github.com/zyfjeff/C-HOW-TO/blob/master/c-malloc/Malloc_tutorial.pdf) – David C. Rankin Apr 28 '19 at 07:03
  • Also, as written, `(struct x){ 42 };` and `(struct y){ { 42 } };` are *compound literals* which written alone make it difficult to understand the context. Where and how are you trying to use them in your code? – David C. Rankin Apr 28 '19 at 07:18
  • [A compound literal is an lvalue.](http://port70.net/~nsz/c/c11/n1570.html#6.5.2.5p4) – cpplearner Apr 28 '19 at 08:49

2 Answers2

2

The reason the restriction only applies to temporary objects that contain arrays is because the restriction is only relevant for temporary objects that contain arrays -- when you have an unnamed non-lvalue object that does NOT contain an array, you can't get an address from the object; using & is explicitly disallowed. However, when the object contains an array, and you access that array by name, you're implicitly getting the address of the first element of the array. Prior to C11, trying to do ANYTHING with that pointer was undefined behavior. With C11, you can now use the object (and the pointer), you just cannot modify it.


Note that the examples you have in the question (with compound literals) are NOT temporary objects -- compound literals are lvalues, so can have their address taken, and have static storage duration.

The main way you get temprary objects is when you have a function that returns a struct (or union) type -- when you call such a function, the returned value is a temprary object. Since it is not an lvalue, you can't use & on it, but if it contains an array, you can use the array name, and that will implicitly decay to a pointer to the array's first element.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • 1
    I may miss something but I cannot find any statement about explicitly disallowing `&` on temporary objects. For example, I can do `&(struct x) { 42 }` – Max Maier Apr 28 '19 at 08:47
  • @MaxMaier indeed ISO/IEC 9899:201x contains valid examples for using `&` on compund literals here http://port70.net/~nsz/c/c11/n1570.html#6.5.2.5p10 and here http://port70.net/~nsz/c/c11/n1570.html#6.5.2.5p15 so I'm joining your question. Although `(struct x) { 42 }` is not a temporary-lifetime object as it has no array in it – izac89 Apr 28 '19 at 09:43
  • @MaxMaier I believe the answer is that compound literals are lvalues, therefore using `&` is legal. The answer speaks about non-lvalues. The statement says compound literals are lvalues is here http://port70.net/~nsz/c/c11/n1570.html#6.5.2.5p4 – izac89 Apr 28 '19 at 09:56
  • @user2162550 yes you are right! Thanks for clarification. This gave me a headache ;-) – Max Maier Apr 28 '19 at 13:23
1

I'm not completely sure about this but here's what I understand from reading EXP35-C. Do not modify objects with temporary lifetime a few times. This isn't a great language-lawyer answer, but I'll try to explain it in simpler terms.

Normally a C function cannot return an array. However, you (try to) work around this by sticking an array in a structure, and returning that. Consider this example code:

#include <stdio.h>

struct X { char a[8]; };

struct X salutation(void) {
  struct X result = { "Hello" };
  return result;
}

struct X addressee(void) {
  struct X result = { "world" };
  return result;
}

int main(void) {
  printf("%s, %s!\n", salutation().a, addressee().a);
  return 0;
}

In C99, this program invoked undefined behavior. You're not allowed to even access the array inside of the structure. The reason is that the lifetime of the array ends when the function with the local variable returns.

In C11, they relaxed the rules a bit so you're allowed to access the array, but you're not allowed to modify it.

Because of this subtle difference, that EXP35-C rule recommends not doing either. Instead, save the array-containing structure result to a local variable:

#include <stdio.h>

struct X { char a[8]; };

struct X salutation(void) {
  struct X result = { "Hello" };
  return result;
}

struct X addressee(void) {
  struct X result = { "world" };
  return result;
}

int main(void) {
  struct X my_salutation = salutation();
  struct X my_addressee = addressee();

  printf("%s, %s!\n", my_salutation.a, my_addressee.a);
  return 0;
}
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • 2
    What should have happened to fix this and many corner cases would have been to recognize that array-to-pointer decay only happens with array lvalues, but also recognize that the use of the subscript operator on arrays accesses the element as a direct operation which doesn't involve array decay. – supercat May 21 '19 at 20:44