首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在C中不使用“打包”编译器指令来打包数据

在C中不使用“打包”编译器指令来打包数据
EN

Stack Overflow用户
提问于 2016-12-12 10:52:03
回答 2查看 722关注 0票数 0

背景:它是一个16位TI数字信号处理器(准确地说是TMS320F2812)。DSP是小终端。编译器是C2000 (它不支持打包指令)。我需要通过以太网通信不同大小的几个结构,从源到目的地。该协议的问题是需要打包数据(填充字节也将被视为信息)。

我目前计划做的事情:在结构中使用Bitfield

代码语言:javascript
复制
typedef struct
{
   INT8U channel:4;
   INT8U priority:4;
   INT16U length;
   INT16U address;
   INT8U array[4];
} _MBXh;

疑问:在这个特殊的结构中,"INT16U长度“将从一个新的对齐内存地址开始(如果我的理解是错误的,请纠正我)。因此,在“4+4优先级”之后将出现(16位-(INT8U)位=) 8位的填充。

即使使用编译器指令“->”,也会发生这种情况吗?(当然,这取决于编译器,但我的问题与我找不到信息的c99标准有关)。我发现c99提供了紧凑的包装。但是有了这个定义,我不明白"INT16U长度“是在"INT8U优先级”之后还是在8位填充之后立即开始。 结构中的Q2 ->数组不能分配位字段。如果数组中有8位元素,则数组中的每个元素都将填充8位,以便与16位处理器对齐。

另一种解决方案是使用char*指针在传输(或接收)时指向结构。

在本例中,Q3 ->需要手动“组合”INT8U通道和INT8U优先级。如果有很多的结构,这就变得困难了。如果这种理解是错误的,请纠正我。 Q4 ->请帮我找到一个更优雅的解决方案。我需要打包数据(包括结构中的位字段和数组),但我没有编译器指令。

EN

回答 2

Stack Overflow用户

发布于 2016-12-12 11:47:17

正如注释中所描述的那样,您应该将结构注释序列化(在写上)和反序列化(在读取时)到/从字节缓冲区。

有许多方法可以做到这一点。例如,内联函数(C99 static inline)、预处理宏、每个字段的单个函数、到位包字段的泛型函数等等。

最常见的选项是将字节数组从内部结构打包并解压缩到内部结构。例如,对于内部使用的结构

代码语言:javascript
复制
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];
}

这特别有用,因为它使得指定字节顺序变得非常简单;上面为lengthaddress字段使用了大端或网络字节顺序。

如果目标系统受到很大的RAM限制,使用预处理器宏直接访问“打包”字段,通常是一个很好的选择。这使用更少的内存,但使用更多的CPU资源。(请注意,“打包”字段在这里也使用大端或网络字节顺序。)

代码语言:javascript
复制
#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位字段,

代码语言:javascript
复制
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;
}

接下来,使用上面的帮助函数编写字段访问器:

代码语言:javascript
复制
#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位于四个高位。

总的来说,我推荐桌面应用程序的第一个选项(每个函数调用的结构转换),以及使用内部结构的情况。对于微控制器和其他内存受限的情况,我推荐这个最新的。

(上述代码均未进行测试。如果您发现了打印错误或其他错误,请在注释中通知我,这样我就可以修复上面的示例代码了。)

票数 1
EN

Stack Overflow用户

发布于 2016-12-12 11:08:12

例如,您应该序列化数据。

代码语言:javascript
复制
static unsigned char * Ser_uint16_t(unsigned char *p, uint16_t value)
{
    *p++ = (value >> 8u) & 0xFFu;
    *p++ = (value >> 0u) & 0xFFu;

    return p;
}

void serialize( INT8U channel,
                INT8U priority,
                INT16U length,
                INT16U address,
                INT8U array[4]
                unsigned char *out_buffer)
{
    out_buffer++ = ((channel & 0x0F) << 4) | (priority & 0x0F);
    out_buffer = Ser_uint16_t(out_buffer, length);
    out_buffer = Ser_uint16_t(out_buffer, address);
    memcpy(out_buffer, array, 4);
}

并在另一边反序列化,就像

代码语言:javascript
复制
static uint16_t DeSer_uint16_t(unsigned char **p)
{
    uint16_t x = 0;

    x |= ((uint16_t)(*p)[0]) <<  8u;
    x |= ((uint16_t)(*p)[1]) <<  0u;

    *p += 2;

    return x;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41099191

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档