cstructconstantsimmutabilitystack-memory

Is it valid to initialize a struct with const members allocated on the stack with alloca?


Considering that the type A is defined like this:

typedef struct a { const int a; } A;

I know that this code is valid:

A * foo = malloc(sizeof *foo); // Allocates uninitialized memory with no effective type
memcpy(foo, &(typeof(*foo)){ .a = 42, }, sizeof *foo); // Effectively initialize the value once

(see https://stackoverflow.com/a/79012045/13242312)

But is it still valid with alloca instead of malloc if we want the value to be on the stack instead of the heap ?

Use case: I want a single return path in my function, so I want to define the result variable at the function scope

A A_new(void) {
  A * a = alloca(sizeof *a);

  if (/* something */) {
    memcpy(a, &(typeof(*a)){.a = 1}, sizeof *a);
  } else {
    memcpy(a, &(typeof(*a)){.a = 2}, sizeof *a);
  }

  return *a;
}

Solution

  • No Difference From Alloca

    Whether alloca or malloc is used has no effect on the relevant semantics, except that alloca is not a standard C library function.

    No Concern About Type

    You can avoid semantic questions about const by not using the destination type while preparing the memory. For example:

    A A_new(void)
    {
        typedef typeof (A_new()) T; //  Define type T for brevity.
    
        void *p = alloca(sizeof (T));
        if (!p) HandleError();
    
        //  Memory is copied as bytes; the type T has not yet been applied to it.
        if (something)
            memcpy(p, & (T) { . a = 1 }, sizeof (T));
        else
            memcpy(p, & (T) { . a = 2 }, sizeof (T));
    
        //  Return an object of type T.
        return * (T *) p;
    }
    

    The accessing of memory as type T in the return statement is defined by the C standard because the memory has been given effective type T by copying into it the bytes of a T object, so it is accessed with its effective type.

    No Need For Allocation

    Your sample code does not illustrate any need to use dynamic allocation, from either malloc or alloca. The entire function body could be just:

    return (typeof (A_new())) { .a = something ? 1 : 2 };
    

    I realize the sample code may be overly simplified, so that perhaps more than just .a = something ? 1 : 2 is needed in the original code. However, the dynamic allocation could still be avoided by recording the member values in their own objects or a parallel structure without const members, such as:

    A A_new(void)
    {
        struct { int a; } Temporary;
    
        if (something)
            Temporary.a = 1;
        else
            Temporary.a = 2;
    
        return (typeof (A_new()) { .a = Temporary.a };
    }