c ++制作具有未知大小数组的结构(内存位置完好无损)以传递缓冲区数据

c++ making struct with unknown size array (with memory location intact) for passing buffer data

所以,我需要以特定方式布置内存:

struct aPacketWithMoreData{
    double x;
    int16 h;
    int16 noNeedToBePassedToTheBuffer;
    double y;
}
struct aPacket{
    double x;
    double y;
    int16 h;
}
struct bufferData{
    int32 something;
    uint64 anotherthing;
    aPacket arrayOfData[]; //with unknown size or runtime size
    float32 somethingThatEndThis;
    int16 withMaybeWeirdSizeAlinement;
}

然后我有一个 aPacketWithMoreData 数组。我需要制作一个临时缓冲区数据,以便我可以调用 API (非常慢)一次来复制缓冲区。做这个的最好方式是什么? (请注意,存储 std::vector 没有帮助,因为 std::vector 实际上只是一个指向数组的指针,而在 API 的另一端,它将无法删除-参考那个。)

我知道在 C++ 中你可以这样做:(link: https://en.cppreference.com/w/c/language/struct)

struct s{
    int a;
    float b;
    double c[]; //array of unknown size
}
...
void function(uint numberOfC) {
    struct s objectName = malloc(sizeof (struct s) + sizeof(double)*numberOfC);
    ...
}

但我相信这是为了 c 的可移植性。当数组位于中间时(缓冲区结构需要),这也不起作用。那么除了像下面这样破解它之外,还有更好的方法吗?

void function(uint number) {
    char* buffer = new char[sizeof(int32)+sizeof(uint64)+sizeof(float32)+sizeof(int16)+sizeof(aPacket)*number];
    *(int32*)(buffer[0]) = returnsInt32();
    *(uint64*)(buffer[sizeof(int32)]) = returnsuint64();
    ...
    size_t offset = sizeof(int32)+sizeof(uint64);
    for (uint i=0; i<number; i++) {
        aPacketWithMoreData& obj = aVector[i]; //inore the possible out of bound for now
        *((aPacket*)(buffer[offset])+i) = aPacket(obj.x,obj.y,obj.h);
    }
    ...
    AnAPI.passData(buffer, sizeof(buffer));
}

编辑:修复了字符数组问题。

这不是为了与 C 兼容,而是标准之外的扩展。无法表示未确定的数组大小,至少在标准中没有定义。在 C 或 C++ 中,类型在编译时是已知的,这意味着它的内存布局是已知的:类型的大小和每个元素的 offsets\memory 位置。

Windows 等一些平台专门调整了 C++ 编译器以支持现有 API,但这不是标准保证,但在更高版本中你不能使用 [] ,而是使用 [1]。典型的 Windows API 结构,变量 length:

typedef struct _SYMBOL_INFO {
  ULONG   SizeOfStruct;   // size of structure, a tradition safety measure in API
  ULONG   TypeIndex;
  ULONG64 Reserved[2];
  ULONG   Index;
  ULONG   Size;
  ULONG64 ModBase;
  ULONG   Flags;
  ULONG64 Value;
  ULONG64 Address;
  ULONG   Register;
  ULONG   Scope;
  ULONG   Tag;
  ULONG   NameLen;
  ULONG   MaxNameLen;   // length of Name in bytes.
  CHAR    Name[1];
} SYMBOL_INFO;

在 C++ 中使用这种结构需要这样的东西:

    // buffer for SYMBOL_INFO with string of 1024 bytes.
    ULONG64 buffer[ (sizeof( SYMBOL_INFO ) + TRACE_MAX_FUNCTION_NAME_LENGTH 
               + sizeof( ULONG64 ) - 1) / sizeof( ULONG64 ) ];
    
    SYMBOL_INFO *info = new (buffer) SYMBOL_INFO {};
    info->SizeOfStruct = sizeof( SYMBOL_INFO );
    info->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;

您的结构需要一分为二。

struct bufferData{
    int32 something;
    uint64 anotherthing;
    uint   lengthOfArray;   // length of following array
    aPacket arrayOfData[1]; // varadic size
};
struct bufferFooter{
    float32 somethingThatEndThis;
    int16 withMaybeWeirdSizeAlinement;
};

请注意 somethingThatEndThis 可能有对齐要求,所以 bufferFooter 也有。

// add number of packets to size of this buffer, by default it's one.
uint64_t *buffer  = new uint64_t[ (sizeof(bufferData)+sizeof(bufferFooter) 
    + (sizeof(uint64_t)-1))/sizeof(uint64_t) ]; 

bufferData* dptr = new (&buffer) bufferData {};
bufferData* fptr = new (&buffer[sizeof(bufferData)/sizeof(uint64_t)]) bufferFooter {}; 
dptr->lengthOfArray = 1;

这按 64 位字对齐结构。如果需要按 char 对齐,这可能会有点复杂,除非使用 memcpy 或平台支持自由对齐。对于从外部源“接收”这种结构,只要结构是 POD,reinterpret_cast 就是一个合法的工具。

如果这些结构是某些二进制文件结构或网络协议的一部分,极度需要快速提取数据,通常的做法是添加 header,这将包括那些“游荡”结构超出变量数组的偏移量. .bmp 或 .png 文件的结构就是很好的例子。在提取时间不成问题的情况下,许多协议转移到 xml-like 结构、二进制表示或文本。