2012-12-03 27 views
0

需要联合结构的帮助。我正在接收包含各种数据包的字节流,所以我将字节数据放入联合结构中,并通过结构成员访问所需的数据。问题在于uint32_t类型的成员 - 读取通过其成员访问时跳过其两个字节并显示错误的值。下面是完整的演示代码:联合结构:打印类型为uint32_t的结构成员跳过两个字节并输出错误值

PacketUtils.h

#include <stdint.h> 

typedef struct { 

    uint8_t startSymbol; 
    uint8_t packetType; 
    uint32_t deviceId; 
    uint16_t packetCRC; 

} PacketData; 

typedef union { 

    uint8_t *bytes; // stores raw bytes 
    PacketData *packet; 

} Packet; 

// Puts bytes into predefined struct 
void getPacketFromBytes(void *bytes, Packet *packetRef); 

PacketUtils.c

#include <stdio.h> 
#include "UnionStruct.h" 

void getPacketFromBytes(void *bytes, Packet *packetRef) 
{ 
    uint8_t *rawBytes = (uint8_t *)bytes; 
    packetRef->bytes = rawBytes; 
} 

调用代码:

// sample byte data 
uint8_t packetBytes[] = {0x11, 0x02, 0x01, 0x01, 0x01, 0x03, 0xbb, 0xbd}; 

Packet packetRef; 
getPacketFromBytes(packetBytes, &packetRef); 

printf("%x\n", packetRef.packet->startSymbol); // good - prints 0x11 
printf("%x\n", packetRef.packet->packetType); // good - prints 0x02 
printf("%x\n", packetRef.packet->deviceId); // bad - prints bd bb 03 01 
printf("%x\n", packetRef.packet->packetCRC); // bad - prints 36 80 (some next values in memory) 

一切都OK时,分组数据结构包括uint8_t或uint16_t的类型成员然后打印显示正确的值。但是,打印uint32_t类型的deviceId将跳过两个字节(0x01 0x01)并获取最后4个字节。打印packetCRC打印给定字节数组的值 - 内存中的一些值,如packetBytes [12]和packetBytes [13]。我不知道为什么它会跳过两个字节...

回答

1

这个问题是由于到在您的平台上被填充为默认对齐的字段。在大多数现代体系结构中,读/写32位字对齐地址时,32位值的效率最高。

在gcc中,您可以通过使用特殊属性来指示结构是“打包”来避免这种情况。在这里看到:

http://gcc.gnu.org/onlinedocs/gcc-3.3.6/gcc/Type-Attributes.html

所以结构定义会是这个样子:

typedef struct { 

    uint8_t startSymbol; 
    uint8_t packetType; 
    uint32_t deviceId; 
    uint16_t packetCRC; 

} PacketData __attribute__((packed)); 
+0

谢谢。我正在编写c函数以便在iOS和Android平台上重用它。但是,iOS的Xcode目前使用LLVM编译器,Android使用GCC。 LLVM中的数据包结构语法不同,但尚未尝试,但我们需要使用< >符号进行换行。在这种情况下恕我直言代码将不可移植... – Centurion

+0

gcc引入__attribute __((packed))语言扩展,但似乎llvm-clang也采用了它。在“Clang Language Extension”中,它说:“除了这里列出的语言扩展外,Clang还希望支持广泛的GCC扩展。”我会试试看。 –

+0

我已经这样做了,但是,在上面的代码示例中,我在Xcode中获得了“'packed'属性忽略”警告。我正在使用Xcode 4.5.1 for iOS,它使用Apple LLVM 4.1编译器。 – Centurion

1

32位数字将仅在4字节边界上对齐。如果你将它移动到你的结构的开始,它可能只是你想要的。

处理器通常经过优化以获取数据的多倍数据大小 - 32字节为4字节,64位为8字节......并且编译器知道这一点并在数据结构中增加了空隙以确保处理器可以高效地获取数据。

如果你不想处理填充和不能走动的数据结构,你可以定义

typedef struct { 
    uint8_t startSymbol; 
    uint8_t packetType; 
    uint16_t deviceIdLow; 
    uint16_t deviceIdHigh; 
    uint16_t packetCRC; 
} PacketData; 

,然后就写

uint32_t deviceID = packetRef.packet->deviceIdLow | (packetRef.packet->deviceIdLow << 16); 
+1

虽然我是否有足够的低,以正确的顺序高是50%的机会... – emrys57