使用可变长度数组成员分配struct

时间:2011-10-03 23:10:32

标签: c++ new-operator c++11 dynamic-memory-allocation

我知道我可以new char[n]创建一个n个字符数组。即使n不是编译时常量,这也适用。

但是我想说我想要一个大小变量,然后是n个字符:

我对此的第一次尝试如下:

struct Test
{
  std::size_t size;
  char a[];
};

但似乎new Test[n]没有达到我的预期,而是分配n size s。

我还发现sizeof(std::string)在ideone处是4,因此它似乎可以在一个块中分配size和char数组。

有没有办法实现我所描述的(大概是std::string已经做过的事情)?

5 个答案:

答案 0 :(得分:11)

虽然可以执行此操作(并且它经常在C中用作排序的解决方法),但建议不要这样做。但是,如果那个真的你想做什么......这是大多数编译器(包括那些不能很好地使用C99增强功能的编译器)的方法。

#define TEST_SIZE(x) (sizeof(Test) + (sizeof(char) * ((x) - 1)))

typedef struct tagTest
{
    size_t size;
    char a[1];
} Test;

int elements = 10; // or however many elements you want
Test *myTest = (Test *)malloc(TEST_SIZE(elements));

C99之前的C规范不允许结构中的零长度数组。要解决此问题,将创建一个包含单个元素的数组,并将一个小于所请求元素数的数组添加到实际结构的大小(大小和第一个元素)以创建预期大小。

答案 1 :(得分:9)

您可以使用placement new

#include <new>

struct Test {
    size_t size;
    char a[1];

    static Test* create(size_t size)
    {
        char* buf = new char[sizeof(Test) + size - 1];
        return ::new (buf) Test(size); 
    }

    Test(size_t s) : size(s)
    {
    }

    void destroy()
    {
        delete[] (char*)this;
    }
};

int main(int argc, char* argv[]) {
    Test* t = Test::create(23);
    // do whatever you want with t
    t->destroy();
}

答案 2 :(得分:7)

您还可以使用“长度1阵列”技巧。这是在C:

struct Test {
    size_t size;
    char a[1];
}

int want_len = 2039;
struct Test *test = malloc(sizeof(struct Test) + (want_len - 1));
test->size = want_len;

GCC也支持“0长度”数组,正是出于这个目的: http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

答案 3 :(得分:2)

如果我理解正确,你需要一个存储指向动态分配的长度前缀字符串的指针的类。你可以利用char*可以安全地别名的事实来做到这一点。

一个简单的实现,只是为了展示it can be done

的方式
class LPS
{
private:
    char* ptr;

public:
    LPS() noexcept : ptr(nullptr) {} // empty string without allocation
    explicit LPS(std::size_t len) {
        // Allocate everything in one go
        // new[] gives storage aligned for objects of the requested size or less
        ptr = new char[sizeof(std::size_t) + len];
        // Alias as size_t
        // This is fine because size_t and char have standard layout
        *reinterpret_cast<std::size_t*>(ptr) = len;
    }
    explicit LPS(char const* sz) {
        std::size_t len = std::char_traits<char>::length(sz);
        ptr = new char[sizeof(std::size_t) + len;
        *reinterpret_cast<std::size_t*>(ptr) = len;
        std::copy(sz, sz + len, ptr + sizeof(std::size_t));
    }
    LPS(LPS const& that) {
        if(that.ptr) {
            ptr = new char[sizeof(std::size_t) + that.size()];
            std::copy(that.ptr, that.ptr + sizeof(std::size_t) + that.size(), ptr);
        } else ptr = nullptr;
    }
    LPS(LPS&& that) noexcept {
        ptr = that.ptr;
        that.ptr = nullptr;
    }
    LPS& operator=(LPS that) {
        swap(that);
        return *this;
    }
    ~LPS() noexcept {
        // deleting a null pointer is harmless, no need to check
        delete ptr;
    }
    void swap(LPS& that) noexcept {
        std::swap(ptr, that.ptr);
    }
    std::size_t size() const noexcept {
         if(!ptr) return 0;
         return *reinterpret_cast<std::size_t const*>(ptr);
    }
    char* string() noexcept {
         if(!ptr) return nullptr;
         // the real string starts after the size prefix
         return ptr + sizeof(std::size_t);
    }
};

答案 4 :(得分:1)

让我们使用std::vector在C ++中保持简短和甜蜜。

struct Test
{
   std::size_t size;
   char *a;  // Modified to pointer

   Test( int size ): size(size), a(new char[size+1])
   {}
};

std::vector<Test> objects(numberOfObjectsRequired,argumentToTheConstructor);