我正在将其作为学习练习。我正在研究的C++书籍将缓冲区作为一种易于操作和流式传输的结构。一切似乎都很好,直到我尝试使用数组(body)并在分配值之后查看缓冲区中的二进制数据。它与我的期望不符。

#include <iostream>
#include <bitset>
#include <netinet/in.h>

using namespace std;

struct dataStruct
{
    uint32_t header;
    uint32_t *body;
};

int main(int argc, char* argv[])
{
    int size, streamSize;

    // 4 bytes per size + 4 bytes for header
    size = 1;
    streamSize = (size * 4) + 4;

    // Create a stream of bytes of appropriate size
    uint8_t *buffer = new uint8_t[streamSize];

    // Cast stream as structure
    dataStruct *sStream = (dataStruct *)buffer;

    // Populate structure with nice 101010... binary patterns
    sStream->header = 2863311530;
    sStream->body = new uint32_t[1];
    sStream->body[0] = 2863311530;
    cout << "Struct: " << sStream->header << ", " << sStream->body[0] << endl;

    // Look at raw data in stream
    for (int i=0; i<sizeof(buffer); i++)
    {
        std::bitset<8> x(buffer[i]);
        cout << "[" << i << "]->" << x << endl;
    }

    return 0;
}

输出为:
Struct: 2863311530, 2863311530
[0]->10101010
[1]->10101010
[2]->10101010
[3]->10101010
[4]->00000000
[5]->00000000
[6]->00000000
[7]->00000000

为什么索引4-7与0-3不同? sStream-> header和sStream-> body都包含相同的值。它们被映射到缓冲区。这是因为body是数组吗?如果是这样,在使用数组时,我将如何操作流以使其工作?

谢谢

最佳答案

您在以下位置使用未初始化的可变size:

streamSize = (size * 4) + 4;

此后取决于streamSize的所有内容都是可疑的,并且是导致未定义行为的原因。

更新

即使在将size初始化为1之后,仍然存在问题。让我逐步讲解代码以及它如何影响您分配的内存。

执行该行之后:
uint8_t *buffer = new uint8_t[streamSize];

你有buffer指向这样的内存:
buffer
|
v
+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+

执行该行之后:
dataStruct *sStream = (dataStruct *)buffer;

您的sStream指向相同的内存,例如:
sStream
|
v
+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+

如果您的编译器未向dataStruct的成员添加任何填充(最佳情况),则将具有:
sStream.header  sStream.body
|               |
v               v
+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+

如果您的编译器在dataStream.header中添加了填充,sStream.body将指向其他内容。最坏的情况:您有一个64位编译器。它将32位填充添加到dataStream.header。在这种情况下,您将拥有:
sStream.header                  sStream.body
|                               |
v                               v
+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+

然后,当您尝试向sStream.body分配任何内容时,最终将使用未授权的内存,例如:
sStream->body = new uint32_t[1];

最好的情况是,您有32位编译器,并且dataStream.header中没有添加任何填充。看起来您有一个64位编译器。即使您的编译器未在dataStream.header中添加任何填充,如果sizeof(void*)是64位,您仍在查看内存溢出问题,我认为您确实这样做。

让我们以32位编译器的最佳情况为例,该编译器不添加任何填充,并且sStream的成员指向分配的内存,例如:
sStream.header  sStream.body
|               |
v               v
+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+

执行该行之后:
sStream->header = 2863311530;

内存看起来像:
sStream.header  sStream.body
|               |
v               v
+---+---+---+---+---+---+---+---+
| 2863311530    |               |
+---+---+---+---+---+---+---+---+

执行该行之后:
sStream->body = new uint32_t[1];

内存看起来像:
sStream.header  sStream.body
|               |
v               v
+---+---+---+---+---+---+---+---+
| 2863311530    |  SomeMemory   |
+---+---+---+---+---+---+---+---+

SomeMemory
|
v
+---+---+---+---+
|               |
+---+---+---+---+

执行该行之后:
sStream->body[0] = 2863311530;
SomeMemory被填充,看起来像:
SomeMemory
|
v
+---+---+---+---+
| 2863311530    |
+---+---+---+---+

我认为您惊讶地发现buffer所指向的内存看起来并不像:
buffer
|
v
+---+---+---+---+---+---+---+---+
| 2863311530    | 2863311530    |
+---+---+---+---+---+---+---+---+

我希望现在知道为什么没有这样做。

关于c++ - 将缓冲区转换为结构,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24372555/

10-10 07:24