c++undefined-behaviorc++23flexible-array-memberstack-allocation

How do I create and use a stack-allocated struct ending with a C99 flexible array member in C++ without undefined behaviour?


I'm calling a 3rd-party C99 library from modern C++. The API contains a struct that ends with a flexible array member. To allocate the struct on the stack I think I need a byte buffer then cast it to the struct.

Is my use of reinterpret_cast well-defined (i.e., is not undefined behavior)?

#include <cstddef>
#include <print>

// 3rd-part C99 API
extern "C" {
  struct Foo {
    int length;
    int array[];
  };

  void do_something(struct Foo*);
}

// My C++ code
auto bar() {
  auto length = 4;
  std::byte buffer[sizeof(Foo) + length * sizeof(int)];
  auto &foo = *reinterpret_cast<Foo *>(buffer); // ← Is this UB?
  foo.length = length;
  do_something(&foo); // ← Is this UB?
  std::println("Result: {}", foo.array[3]); // ← Is this UB?
}

Edit: I'm compiling with -std=gnu++23 so flexible array members will compile.


Solution

  • IMO it is UB. But you can do something like this (VLAs are not portable in C++ and it is gcc-centric):

    #include <cstddef>
    #include <new>
    #include <print>
    
    extern "C" {
      struct Foo {
        int length;
        int array[]; // non-portable C++ extension
      };
      void do_something(struct Foo*);
    }
    
    auto bar() {
      int length = 4;
    
      // Ensure proper alignment for Foo
      alignas(Foo) std::byte storage[sizeof(Foo) + length * sizeof(int)];
    
      // Create a Foo object in that storage
      auto* foo = std::construct_at(reinterpret_cast<Foo*>(storage));
      foo->length = length;               // initialize header before calling C
    
      do_something(foo);                  // now OK (alignment + lifetime are fine)
    
      // OK if do_something wrote at least 4 ints
      std::println("Result: {}", foo->array[3]);
    
      std::destroy_at(foo);               // trivial, but symmetric
    }
    

    https://godbolt.org/z/Kr5zxfqWj