使用memcpy进行结构化

时间:2013-10-09 09:55:46

标签: c struct memcpy

在结构上使用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代码,则前两个变量(前导码和长度)将被复制。控件完全搞砸了,最后三个变量向上移动了一个,也就是crc = 0,source = crc,destination = source ......

ANyone对我有什么好建议吗?

5 个答案:

答案 0 :(得分:2)

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

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

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

实现正确的序列化/反序列化功能:

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;
}

注意这需要明确关于序列化数据的字节顺序,这是你一直应该关心的事情(我使用big-endian)。它还显式构建了uint8_t字段的control个版本,假设使用了结构版本。

另请注意,CONTROL声明中有拼写错误; fcb发生两次。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

检查sizeof(struct CONTROL) - 我认为它将是2或4,具体取决于机器。由于您使用unsigned位域(并且unsignedunsigned int的简写),整个结构(struct CONTROL)至少需要无符号整数的大小 - 即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;
}

答案 4 :(得分: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堆栈。