Search code examples
ccompound-literals

storage duration of compound literals


I somehow can't comprehend how the storage duration of compound literals defined in blocks is automatic, and the reasoning is as follows:

let us assume that the compound literal is defined in a function or block that is called repeatedly; when this function is called for the first time, how can the computer create the literal if it is not in static memory to begin with?? ( what I mean is how does he know its value?? for example is it (int [2]) {2,4} or (int [5]) {5,4,2,1,4}) and if it somehow exists anywhere, how does the computer know its contents again? ( when he tries to construct it again for the second and every subsequent call) after its first pass away.

the case with other literals such as string literals and normal literals is that they are in static memory, and this is very reasonable because how can the computer know its value if he hasn't stored this value somewhere.

can anyone explain this properly to me??


Solution

  • Perhaps calling the object a "compound literal" is a bit misleading. It is actually not really so literal.

    It's helpful to consider an actual example, even if it's a bit dumb:

    /* This is NOT the way to solve this problem */
    double cubic(double x, double a, double b, double c, double d) {
      double* powers = (double []){1, x, x*x, x*x*x};
      double* coefficients = (double []){a, b, c, d};
      double sum = 0;
      for (int i = 0; i < 4; ++i) sum += powers[i]*coefficients[i];
      return sum;
    }
    

    There are two compound literals in that function, and it is clear that neither of them could be pre-constructed, since they depend on function arguments.

    Fortunately, the C compiler is a compiler. It is not limited to creating new datastructures from a copy of an existing constant. It can produce code which allocates the two arrays on the stack ("automatic storage") and then fills them in appropriately.

    It's not difficult for the compiler to allocate stack space for these compound literals, because it knows exactly how big they are. In effect, the code produced would be just the same as if I had written:

    double cubic(double x, double a, double b, double c, double d) {
      double _powers_values_[4];
      _powers_values_[0] = 1;
      _powers_values_[1] = x;
      _powers_values_[2] = x*x;
      _powers_values_[3] = x*x*x;
      double* powers = _powers_values_;
      // ...
    

    and that is pretty well what you would see if you looked at the generate code for the original function.

    Also note that both powers and coefficients are mutable, so I could have modified them in the function:

    /* This is NOT the way to solve this problem */
    double cubic(double x, double a, double b, double c, double d) {
      double* powers = (double []){1, x, x*x, x*x*x};
      double* coefficients = (double []){a, b, c, d};
      for (int i = 0; i < 4; ++i) coefficients[i] *= powers[i];
      for (int i = 1; i < 4; ++i) coefficients[i] += coefficients[i+1];
      return coefficients[3];
    }
    

    Of course, a compound literal might just have constant values:

    double* coefficients = (double []){17, 6, -3, 2.5};
    

    But as written, that array is still mutable, so the compiler needs to arrange for the function to have a new copy of the values. If I wanted to, I could make it clear that the array is immutable:

    const double* coefficients = (const double []){17, 6, -3, 2.5};
    

    Now the compiler is allowed to use a static literal, instead of making an unnecessary copy. But, in theory, the compound literal still has automatic scope, and returning a pointer to it from the function would be undefined behaviour.