【问题标题】:Cast buffer to structure pointer将缓冲区转换为结构指针
【发布时间】:2018-04-19 19:43:56
【问题描述】:

我有一个缓冲区,想将其转换为指向结构的指针:

#include <stdint.h>
#include <stdio.h>

struct __attribute__((packed)) req {
    uint8_t a;
    uint16_t b;
    uint8_t *c;
};


void f(uint8_t *Buf)
{
    struct req *r = (struct req *)Buf;

    printf("a: %02x\n", r->a);
    printf("b: %04x\n", r->b);
    printf("c: %02x\n", r->c[0]);

}

int main()
{
    uint8_t buf[] = {0x01, 0x02, 0x03, 0x04 };

    f(buf);

    return 0;
}

这个例子不起作用,因为第 4 个字节不是地址,而是实际数据。我可以通过手动设置指针来“解决”这个问题:

r->c = &Buf[3];

有没有办法通过演员表做到这一点?

【问题讨论】:

  • 我不知道指针是单字节的任何硬件,所以对于初学者来说,缓冲区太小了。并且使指针指向自身并不能解决任何问题。它更像是断腿上的创可贴。您真正想在这里做什么?
  • 如果你想在结构体中添加一个数组,也许你的意思是uint8_t c[1];
  • 不,这是未定义的行为。通过分配给r-&gt;c,您既破坏了您试图指向的缓冲区内容,又写入了数组buf 的末尾。在f,你可以做struct rec r;r.a = Buf[0];r.b = Buf[1] | (Buf[2] &lt;&lt; 8); /* assuming little-endian */r.c = &amp;Buf[3];
  • 除了别人说的,通过不兼容的struct成员访问字节数组是一种严格的别名冲突,也是未定义的行为。不要那样做。
  • 你期望什么输出?

标签: c pointers struct


【解决方案1】:

有没有办法通过演员表做到这一点?

不,有很多原因:

  • 如你所说,字节流与结构不对应。最后一项不是指针。只有当您知道存储在目标位置的实际数据与目标变量的类型相同时才能使用强制转换。
  • 强制转换调用未定义的行为,因为它违反了the strict aliasing rule。任何事情都有可能发生。
  • 字节流中的各种数据似乎未对齐,这可能是也可能不是问题,具体取决于系统。

在这种情况下,您必须手动反序列化数据:

struct req r = { buf[0], 
                 (uint16_t)buf[1]<<8 | buf[2], // assumes big endian
                 &buf[3] };

在这里我们注意到代码也是依赖于字节序的。因此,您必须知道这些数据来自的系统/协议的字节序,以便正确地对其进行反序列化。

【讨论】:

  • 我做了同样的事情,只是我的系统是 little-endian。
【解决方案2】:

有没有办法通过演员表做到这一点?

是的,有以下假设:

  • 您需要注意结构对齐(特定于编译器):
    #pragma pack(push, 1) ... #pragma pack(pop)
    __attribute__((packed))

  • 您需要注意架构和缓冲区中的字节之间的字节顺序(如果需要进行转换)

您可以做的是在结构中使用void* c 而不是uint8_t* c,然后将void* 强制转换为显式指针类型。

示例 1:

#include <stdint.h>
#include <stdio.h>

#pragma pack(push, 1)
typedef struct {
    uint8_t a;
    uint16_t b;
    void* c;
} req;
#pragma pack(pop)

void f(uint8_t* Buf)
{
    req* r = (req*)Buf;
    printf("a: %02x\n", r->a);
    printf("b: %04x\n", r->b);
    printf("c: %02x\n", ((uint8_t*)&r->c)[0]);
}
int main()
{
    uint8_t buf[] = { 0x01, 0x02, 0x03, 0x04 };
    f(buf);
    return 0;
}

输出:

a: 01
b: 0302
c: 04

示例 2:

#include <stdint.h>
#include <stdio.h>

#pragma pack(push, 1)
typedef struct {
    uint8_t a;
    uint16_t b;
    void* c;
} req;

typedef struct {
    uint8_t cmd;
    uint16_t value;
} SubPacket;
#pragma pack(pop)

void f(uint8_t* Buf)
{
    req* r = (req*)Buf;

    printf("a: %02x\n", r->a);
    printf("b: %04x\n", r->b);

    SubPacket* sub_packet = (SubPacket*)&r->c;

    printf("cmd: %02x\n", sub_packet->cmd);
    printf("value: %04x\n", sub_packet->value);

}

int main()
{
    uint8_t buf[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };

    f(buf);

    return 0;
}

输出:

a: 01
b: 0302
cmd: 04
value: 0605

【讨论】:

【解决方案3】:
#include <stdint.h>
#include <stdio.h>

struct __attribute__((packed)) req {
    uint8_t a;
    uint16_t b;
    uint8_t c[]; /* <--- Use this notation to be able to achieve this */
};


void f(uint8_t *Buf)
{
    struct req *r = (struct req *)Buf;

    printf("a: %02x\n", r->a);
    printf("b: %04x\n", r->b);
    printf("c: %02x\n", r->c[0]);

}

int main()
{
    uint8_t buf[] = {0x01, 0x02, 0x03, 0x04 };

    f(buf);

    return 0;
}

看这里:How to include a dynamic array INSIDE a struct in C?

【讨论】:

  • 它可能正常工作,也可能不工作。 (我没有投反对票)
  • 我认为它可以工作,至少它可以使用指向已分配内存的指针。 stackoverflow.com/questions/2060974/…
  • 这是一个严格的混叠违规
猜你喜欢
  • 2021-03-13
  • 1970-01-01
  • 2015-01-31
  • 2019-02-08
  • 2015-04-15
  • 2013-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多