2013-10-09 77 views
1

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

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

+2

您还应该显示您使用的'memcpy'代码。 – user694733

+0

你确定,在HEADER改变后,接收缓冲区有完全相同的改变? – zoska

回答

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

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

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

+0

您能否介绍一下序列化函数的作用?从来没有使用过类似的东西...... – Aune

+0

当查看指针存储器(指向结构体的指针)时,数据正确地位于那里,但是当查看手表时,数据被吓倒... – Aune

+0

@ user1244472在序列化函数你应该将每个结构成员转换为字节。 – user694733

0

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

0

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

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

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

使用无符号字符x:1;是非法的 – Aune

+0

@ user1244472:不是。阅读有关位域的信息。 – wildplasser

+0

当我尝试这个时,我得到编译器错误“非法位域类型” – Aune

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的用户则可以选择的方式他想要访问这个东西。

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:你也必须检查编译器的bit-endianness。


还要注意的是位域不是非常有用,特别是如果数据发送到线路上,并且代码需要在不同的平台上运行,用不同的排列和/或字节顺序。 Plain unsigned charuint8_t加上一些位掩码产生更干净的代码。例如,检查BSD或Linux内核中的IP堆栈。

+0

单独联合将无法工作,因为'all'和'control'-成员具有不同的大小。 – user694733

+0

你能否详细解释一下如何使用它? – Aune

+0

结构控制是8位 – Aune