1

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;
}
14
  • 1
    foo is a pointer. It doesn't matter where it points or how that memory was allocated, as long as there's enough bytes available. With that said, what is the use-case for you to use alloca instead of just defining a normal local variable? Commented Apr 29 at 7:51
  • 2
    Then what makes you think malloc and alloca makes a difference? Commented Apr 29 at 8:01
  • 1
    @Fayeure, As alloca() has a local lifetime, consider instead using A * foo = &(typeof(*foo)){ .a = 42, }; or A * foo = &(A){ .a = 42, }; which is also good (until the end of the block). Commented Apr 29 at 8:17
  • 1
    @Fayeure alloca() is not part of the standard C library. Posting a use case would be useful to offer good alternatives. Commented Apr 29 at 8:19
  • 5
    Do you really need to avoid definition with initialization? What's wrong with using (non-pointer) A a = { .a = (somecondition) ? 1 : 2, };? Commented Apr 29 at 9:39

1 Answer 1

4

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 };
}
Sign up to request clarification or add additional context in comments.

6 Comments

When you say "You can avoid semantic questions about const by using the destination type while preparing the memory.", I think you're talking about declaring p with type void *. That makes sense, but the wording leaves me a little uncertain, and on its face, it doesn't seem consistent with all the appearances of T prior to the return statement.
@chux: OP used typeof. My current Clang version only has __typeof.
@JohnBollinger: T is present prior to the memcpy call, but it is only used to calculate the size to allocate and to prepare a compound literal as the source object. It has not been used at all with the allocated memory that p points to. So that memory is just untyped memory, copied into byte-by-byte by memcpy. Nothing in it has any const associated, so there cannot be any issue about const interfering with the writes into that memory.
@EricPostpischil OK, I like to imagine the new C23 as something with more fun and flexibility. (Had not seen typeof function before.)
@EricPostpischil, yes, I understand what's going on and why it's consistent with the spec. And it would be consistent, for the same reason, even with p declared as T * (though perhaps more exposed to compiler foibles). I'm saying that the specific sentence I called out is confusing. And if you don't intend for that sentence to mean just declaring p as type void * then it's even more confusing than I thought.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.