正如评论中所描述的展开,您应该将结构注释序列化(写入时)和反序列化(读取时)到/来自字节缓冲区。
有很多方法可以做到这一点。例如,内联函数 (C99 static inline
)、预处理器宏、每个字段的单独函数、位打包字段的通用函数等等。
最常见的选项是将字节数组从内部结构打包和解包到内部结构。例如,对于内部使用的结构
struct mbxh {
INT8U channel:4;
INT8U priority:4;
INT16U length;
INT16U address;
INT8U array[4];
};
static void pack_mbxh(unsigned char *const dst, const struct mbxh *src)
{
dst[0] = src->channel | ((src->priority) << 4);
dst[1] = src->length >> 8;
dst[2] = src->length;
dst[3] = src->address >> 8;
dst[4] = src->address;
dst[5] = src->array[0];
dst[6] = src->array[1];
dst[7] = src->array[2];
dst[8] = src->array[3];
}
static void unpack_mbxh(struct mbxh *dst, const unsigned char *const src)
{
dst->channel = src[0] & 15U;
dst->priority = (src[0] >> 4) & 15U;
dst->length = (src[1] << 8) | src[2];
dst->address = (src[3] << 8) | src[4];
dst->array[0] = src[5];
dst->array[1] = src[6];
dst->array[2] = src[7];
dst->array[3] = src[8];
}
这特别有用,因为它使指定字节顺序变得很简单;length
以上对andaddress
字段使用大端或网络字节顺序。
如果目标系统非常受 RAM 限制,则使用预处理器宏直接访问“打包”字段通常是一个不错的选择。这使用更少的内存,但更多的 CPU 资源。(请注意,“打包”字段在这里也使用大端或网络字节顺序。)
#define mbxh_get_channel(data) ((data)[0] & 15U)
#define mbxh_get_priority(data) ((data)[0] >> 4)
#define mbxh_get_length(data) ((((INT16U)(data)[1]) << 8) | ((INT16U)(data)[2]))
#define mbxh_get_address(data) ((((INT16U)(data)[3]) << 8) | ((INT16U)(data)[4]))
#define mbxh_get_array(data, i) ((data)[i])
#define mbxh_set_channel(data, value) \
do { \
(data)[0] = ((data)[0] & 240U) | ((INT8U)(value)) & 15U); \
} while (0)
#define mbxh_set_priority(data, value) \
do { \
(data)[0] = ((data)[0] & 15U) | (((INT8U)(value)) & 15U) << 4); \
} while (0)
#define mbxh_set_length(data, value) \
do { \
(data)[1] = ((INT16U)(value)) >> 8; \
(data)[2] = (INT8U)(value); \
} while (0)
#define mbxh_set_address(data, value) \
do { \
(data)[3] = ((INT16U)(value)) >> 8; \
(data)[4] = (INT8U)(value); \
} while (0)
#define mbxh_set_array(data, index, value) \
do { \
(data)[(index)] = (INT8U)(value); \
} while (0)
在实践中,特别是如果您有许多这样的结构,这些结构的组合将起作用。首先,您编写一些紧凑的函数来访问每种类型的字段:低半字节、高半字节或 16 位字段,
static INT8U get4u_lo(const INT8U *const ptr)
{
return (*ptr) & 15U;
}
static INT8U get4u_hi(const INT8U *const ptr)
{
return (*ptr) >> 4;
}
static INT16U get16u(const INT8U *const ptr)
{
return (((INT16U)ptr[0]) << 8) | ptr[1];
}
static void set4u_lo(INT8U *const ptr, INT8U val)
{
*ptr &= 240U;
*ptr |= val & 15U;
}
static void set4u_hi(INT8U *const ptr, INT8U val)
{
*ptr &= 15U;
*ptr |= (val % 15U) << 4;
}
static void set16u(INT8U *const ptr, INT16U val)
{
ptr[0] = val >> 8;
ptr[1] = val;
}
接下来,您使用上述辅助函数编写每个结构的字段访问器:
#define mbxh_get_channel(data) get4u_lo((INT8U *)(data)+0)
#define mbxh_get_priority(data) get4u_hi((INT8U *)(data)+0)
#define mbxh_get_length(data) get16u((INT8U *)(data)+1)
#define mbxh_get_address(data) get16u((INT8U *)(data)+3)
#define mbxh_get_array(data, i) ((data)[5+(i)])
#define mbxh_set_channel(data, v) set4u_lo((INT8U *)(data)+0, (v))
#define mbxh_set_priority(data, v) set4u_hi((INT8U *)(data)+0, (v))
#define mbxh_set_length(data, v) set16u((INT8U *)(data)+1, (v))
#define mbxh_set_address(data, v) set16u((INT8U *)(data)+3, (v))
#define mbxh_set_array(data, i, v) ((data)[5+(i)] = (v))
与此答案中的所有示例一样,上面的数据也使用大端或网络字节顺序。channel
在第一个数据字节的四个低位和priority
四个高位中。
总的来说,我建议桌面应用程序的第一个选项(每个函数调用的结构转换),以及无论如何使用内部结构的情况。对于微控制器和其他内存受限的情况,我推荐这个最新的。
(以上代码均未测试。如果发现拼写错误或错误或其他错误,请在评论中通知我,以便我修复上面的示例代码。)