1

在结构上使用 memcpy 时遇到问题。

考虑以下结构

struct HEADER
{
    unsigned int preamble;
    unsigned char length;
    unsigned char control;
    unsigned int destination;
    unsigned int source;
    unsigned int crc;
}

如果我使用 memcpy 将数据从接收缓冲区复制到此结构,则复制是可以的,但如果我将结构重新声明为以下内容:

struct HEADER
{
    unsigned int preamble;
    unsigned char length;
    struct CONTROL control;
    unsigned int destination;
    unsigned int source;
    unsigned int crc;
}

struct CONTROL
{
    unsigned dir : 1;
    unsigned prm : 1;
    unsigned fcb : 1;
    unsigned fcb : 1;
    unsigned function_code : 4;
}

现在,如果我使用与以前相同的 memcpy 代码,则前两个变量( preamble 和 length )被复制好。控件完全搞砸了,最后三个变量上移了一个,即 crc = 0,source = crc,destination = source...

有人对我有什么好的建议吗?

4

5 回答 5

2

你知道接收缓冲区中的格式是正确的,当你control在中间添加时?

无论如何,您的问题是位域在这里是错误的工具:您不能特别依赖内存中的布局,尤其是您为序列化表单选择的完全相同的布局。

尝试直接将结构复制到外部存储或从外部存储复制几乎从来都不是一个好主意。你需要适当的序列化。编译器可以在结构的字段之间添加填充和对齐,使用位域会使情况变得更糟。不要这样做。

实现适当的序列化/反序列化功能:

unsigned char * header_serialize(unsigned char *put, const struct HEADER *h);
unsigned char * header_deserialize(unsigned char *get, struct HEADER *h);

遍历结构并根据需要读取/写入尽可能多的字节(可能针对每个字段):

static unsigned char * uint32_serialize(unsigned char *put, uint32_t x)
{
    *put++ = (x >> 24) & 255;
    *put++ = (x >> 16) & 255;
    *put++ = (x >> 8) & 255;
    *put++ = x & 255;
    return put;
}

unsigned char * header_serialize(unsigned char *put, const struct HEADER *h)
{
    const uint8_t ctrl_serialized = (h->control.dir << 7) |
                                    (h->control.prm << 6) |
                                    (h->control.fcb << 5) |
                                    (h->control.function_code);

    put = uint32_serialize(put, h->preamble);
    *put++ = h->length;
    *put++ = ctrl_serialized;
    put = uint32_serialize(put, h->destination);
    put = uint32_serialize(put, h->source);
    put = uint32_serialize(put, h->crc);

    return put;
}

请注意,这需要明确说明序列化数据的字节顺序,这是您始终应该关心的事情(我使用了大字节序)。假设使用了 struct 版本,它还显式地构建了单个uint8_t版本的字段。control

CONTROL另请注意,您的声明中有错字;fcb出现两次。

于 2013-10-09T10:01:23.090 回答
0

检查sizeof(struct CONTROL)——我认为这将是 2 或 4,具体取决于机器。由于您使用的是unsigned位域(并且unsigned是 的简写unsigned int),因此整个结构(struct CONTROL)至少会占用 unsigned int 的大小——即 2 或 4 个字节。

并且,使用unsigned char control该字段需要 1 个字节。因此,绝对应该与control变量不匹配。

尝试重写struct control如下: -

struct CONTROL
{
    unsigned char dir : 1;
    unsigned char prm : 1;
    unsigned char fcb : 1;
    unsigned char fcb : 1;
    unsigned char function_code : 4;
}
于 2013-10-09T10:57:33.727 回答
0

干净的方法是使用联合,例如:

struct HEADER
{
    unsigned int preamble;
    unsigned char length;
    union {
      unsigned char all;
      struct CONTROL control;
      } uni;
    unsigned int destination;
    unsigned int source;
    unsigned int crc;
};

然后,该结构的用户可以选择他想要访问该事物的方式。

struct HEADER thing = {... };

if (thing.uni.control.dir) { ...}

或者

#if ( !FULL_MOON ) /* Update: stacking of bits within a word appears to depend on the phase of the moon */
if (thing.uni.all & 1) { ... }
#else
if (thing.uni.all & 0x80) { ... }
#endif

注意:这个结构不能解决字节顺序问题,需要隐式转换。

注意2:您还必须检查编译器的位字节序。


另请注意,位域不是很有用,特别是如果数据通过网络传输,并且代码预计将在不同的平台上运行,具有不同的对齐方式和/或字节序。普通unsigned charuint8_t加上一些位掩码会产生更清晰的代码。例如,检查 BSD 或 linux 内核中的 IP 堆栈。

于 2013-10-09T11:07:22.943 回答
0

使用struct CONTROL control;代替unsigned char control;会导致结构内部的对齐方式不同,因此填充它memcpy()会产生不同的结果。

于 2013-10-09T10:01:29.437 回答
0

Memcpy 将字节值从源指向的位置直接复制到目标指向的内存块。

源指针和目标指针指向的对象的底层类型与此函数无关;结果是数据的二进制副本。因此,如果有任何结构填充,那么您将弄乱结果。

于 2013-10-09T10:02:10.603 回答