变量大小的Struct C++

Unk*_*own 20 c++ struct variable-length

这是在C++中制作可变大小结构的最佳方法吗?我不想使用vector,因为初始化后长度不会改变.

struct Packet
{
    unsigned int bytelength;
    unsigned int data[];
};

Packet* CreatePacket(unsigned int length)
{
    Packet *output = (Packet*) malloc((length+1)*sizeof(unsigned int));
    output->bytelength = length;
    return output;
}
Run Code Online (Sandbox Code Playgroud)

编辑:重命名变量名称和更改的代码更正确.

bk1*_*k1e 10

关于你在做什么的一些想法:

  • 使用C风格的可变长度struct idiom允许您为每个数据包执行一次免费存储分配,这是struct Packet包含a时所需数量的一半std::vector.如果您分配一个非常大的数量的数据包,然后进行一半的自由存储分配/释放操作很可能是显著.如果您还在进行网络访问,那么等待网络所花费的时间可能会更加重要.

  • 该结构表示数据包.您是否计划直接从套接字读取/写入struct Packet?如果是这样,您可能需要考虑字节顺序.您在发送数据包时是否必须从主机转换为网络字节顺序,反之亦然?如果是这样,那么您可以在可变长度结构中对数据进行字节交换.如果将其转换为使用向量,则编写用于序列化/反序列化数据包的方法是有意义的.这些方法会将其转移到连续缓冲区或从连续缓冲区转移,并考虑字节顺序.

  • 同样,您可能需要考虑对齐和包装.

  • 你永远不能继承Packet.如果你这样做,那么子类的成员变量将与数组重叠.

  • 而不是mallocfree,你可以使用Packet* p = ::operator new(size)::operator delete(p),因为它struct Packet是一个POD类型,并且目前不会受益于其默认构造函数及其析构函数被调用.这样做的(潜在)好处是全局operator new使用全局新处理程序和/或异常处理错误,如果这对您很重要.

  • 可以使变长度结构惯用法与new和delete运算符一起使用,但不是很好.您可以operator new通过实现创建一个采用数组长度的自定义static void* operator new(size_t size, unsigned int bitlength),但您仍然需要设置bitlength成员变量.如果使用构造函数执行此操作,则可以使用稍微冗余的表达式Packet* p = new(len) Packet(len)来分配数据包.相比于使用全局我看到的唯一的好处operator newoperator delete将是你的代码的客户端可以只调用delete p代替::operator delete(p).将分配/释放包装在单独的函数中(而不是delete p直接调用)只要正确调用它们就可以了.


Nil*_*nck 7

如果您从未添加构造函数/析构函数,则使用malloc/free进行分配的赋值运算符或虚函数是安全的.

它在c ++圈子中不受欢迎,但我认为如果你在代码中记录它就可以使用它.

对您的代码的一些评论:

struct Packet
{
    unsigned int bitlength;
    unsigned int data[];
};
Run Code Online (Sandbox Code Playgroud)

如果我记得正确声明一个没有长度的数组是非标准的.它适用于大多数编译器,但可能会给你一个警告.如果要符合要求,请声明长度为1的数组.

Packet* CreatePacket(unsigned int length)
{
    Packet *output = (Packet*) malloc((length+1)*sizeof(unsigned int));
    output->bitlength = length;
    return output;
}
Run Code Online (Sandbox Code Playgroud)

这可行,但您不考虑结构的大小.将新成员添加到结构后,代码将中断.最好这样做:

Packet* CreatePacket(unsigned int length)
{
    size_t s = sizeof (Packed) - sizeof (Packed.data);
    Packet *output = (Packet*) malloc(s + length * sizeof(unsigned int));
    output->bitlength = length;
    return output;
}
Run Code Online (Sandbox Code Playgroud)

并在数据包结构定义中写入注释,数据必须是最后一个成员.

顺便说一句 - 用一次分配来分配结构和数据是一件好事.您可以通过这种方式将分配数量减半,并且还可以改善数据的局部性.如果分配大量软件包,这可以提高性能.

不幸的是,c ++并没有提供一个很好的机制来做到这一点,所以你经常在现实世界的应用程序中得到这样的malloc/free hacks.

  • sizeof(Packet) 将是对齐要求的倍数,而不是结构的实际大小。例如 `struct Foo { uint64_t magic; uint32_t 长度;uint8_t buf[];}` 也有一个 `sizeof(Foo) == 16` 和 `sizeof(Foo) - sizeof(Foo::buf) == 16`。你想要的大小是“offsetof(Foo, buf) == 12”。 (2认同)

Mar*_*ork 5

这没关系(并且是C的标准做法).

但这对C++来说不是一个好主意.
这是因为编译器会自动为您生成一整套其他方法.这些方法不明白你有欺骗行为.

例如:

void copyRHSToLeft(Packet& lhs,Packet& rhs)
{
    lhs = rhs;  // The compiler generated code for assignement kicks in here.
                // Are your objects going to cope correctly??
}


Packet*   a = CreatePacket(3);
Packet*   b = CreatePacket(5);
copyRHSToLeft(*a,*b);
Run Code Online (Sandbox Code Playgroud)

使用std :: vector <>它更安全,工作正常.
我也打赌它会在优化器启动后与您的实现一样高效.

或者,boost包含一个固定大小的数组:http:
//www.boost.org/doc/libs/1_38_0/doc/html/array.html

  • 如果我使用矢量,那么长度成员不会是不连续的吗? (3认同)
  • 他担心会有第二次分配:new vector <int>(50); 将导致两个分配:一个用于矢量对象,另一个用于由矢量对象维护的50个int的数组. (3认同)

Mat*_*vis 0

如果您真正使用 C++,那么除了默认成员可见性之外,类和结构之间没有任何实际区别 - 类默认情况下具有私有可见性,而结构默认情况下具有公共可见性。以下是等效的:

struct PacketStruct
{
    unsigned int bitlength;
    unsigned int data[];
};
class PacketClass
{
public:
    unsigned int bitlength;
    unsigned int data[];
};
Run Code Online (Sandbox Code Playgroud)

关键是,您不需要 CreatePacket()。您可以简单地使用构造函数初始化结构对象。

struct Packet
{
    unsigned long bytelength;
    unsigned char data[];

    Packet(unsigned long length = 256)  // default constructor replaces CreatePacket()
      : bytelength(length),
        data(new unsigned char[length])
    {
    }

    ~Packet()  // destructor to avoid memory leak
    {
        delete [] data;
    }
};
Run Code Online (Sandbox Code Playgroud)

有几点需要注意。在 C++ 中,使用 new 而不是 malloc。我采取了一些自由措施,将位长度更改为字节长度。如果此类表示网络数据包,那么处理字节而不是位会更好(在我看来)。数据数组是 unsigned char 数组,而不是 unsigned int 数组。同样,这是基于我的假设,即此类代表网络数据包。构造函数允许您创建如下所示的数据包:

Packet p;  // default packet with 256-byte data array
Packet p(1024);  // packet with 1024-byte data array
Run Code Online (Sandbox Code Playgroud)

当 Packet 实例超出范围并防止内存泄漏时,会自动调用析构函数。

  • 您对数据成员的初始化将不起作用,因为它不是指针。如果将其更改为指针,则会丢失长度与数据的连续布局,我认为这是 OP 的目标。 (8认同)