如何在具有8位成员的队列中前进,并将耦合组合为16位值

How to advance in a queue with 8-bit members and combine couples into 16-bit values

本文关键字:耦合 16位值 组合 8位 队列 成员      更新时间:2023-10-16

我有一个指向数组的指针,该数组包含四对8位值,比如这个

MSB-lsb-MSB-lsb-MSB-lsb-MSB-lsb
 ^
 |
bufPointer

我将在这个队列中前进,并用这四个8位对填充fooStruct结构的16位成员。

我可以为第一位成员分两行完成这项工作:

fooStruct.M1 = *bufPointer << 8;
fooStruct.M1 |= *++bufPointer;

等等

所以,如果我们有

MSB = 0x22;
lsb = 0x37;
hence => fooStrct.M1 = 0x2237;

有可能在一个班轮里做到这一点吗?


为什么选择One Liner真的没有什么特别的原因。学习+好奇心/方便时将来使用。:)

是的,你可以在一行中完成,但为了避免序列点警告,最好这样做:

c = (*p << 8) | *(p + 1);

示例:

#include <stdio.h>
#if defined(__LP64__) || defined(_LP64)
# define BUILD_64   1
#endif
#ifdef BUILD_64
# define BITS_PER_LONG 64
#else
# define BITS_PER_LONG 32
#endif
char *binpad (unsigned long n, size_t sz);
int main (void) {
    unsigned char a[] = { 65, 28, 59, 15 };
    unsigned char *p = NULL;
    unsigned char *end = a + sizeof a/sizeof *a;
    unsigned short c = 0;
    for (p = a; p < end; p += 2)
    {
        c = (*p << 8) | *(p + 1);
        printf ("n *p       : %6hhu  (%s)n", *p, binpad (*p, 16));
        printf (" *(p + 1) : %6hhu  (%s)n", *(p + 1), binpad (*(p + 1), 16));
        printf ("n c        : %6hu  (%s)n", c, binpad (c, 16));
    }
    return 0;
}
char *binpad (unsigned long n, size_t sz)
{
    static char s[BITS_PER_LONG + 1] = {0};
    char *p = s + BITS_PER_LONG;
    register size_t i;
    for (i = 0; i < sz; i++)
        *(--p) = (n>>i & 1) ? '1' : '0';
    return p;
}

输出

$ ./bin/uchar2short
 *p       :     65  (0000000001000001)
 *(p + 1) :     28  (0000000000011100)
 c        :  16668  (0100000100011100)
 *p       :     59  (0000000000111011)
 *(p + 1) :     15  (0000000000001111)
 c        :  15119  (0011101100001111)

为什么在一行中完成它如此重要?

无论如何,这是一种方式:

fooStruct.M1 = (bufPointer += 2, (bufPointer[-2] << 8) | bufPointer[-1]);

由于您使用的是C++,因此可以将缓冲区封装在更智能的"读取器"中。

class Reader
{
public:
    Reader(char* bufPointer)
        : m_bufPointer(bufPointer) {}
    short shortBE()
    {
        short result = (m_bufPointer[0] << 8) | m_bufPointer[1];
        m_bufPointer += 2;
        return result;
    }
    short shortLE()
    {
        short result = (m_bufPointer[1] << 8) | m_bufPointer[0];
        m_bufPointer += 2;
        return result;
    }
    // etc...
};

FooStruct DeserializeFooStruct(char* bufPointer)
{
   Reader rd(bufPointer);
   FooStruct fooStruct;
   fooStruct.M1 = rd.shortBE();
   fooStruct.M2 = rd.shortBE();
   // and so on
   return fooStruct;
}

这种方法的一个优点是,您还可以添加错误检查。例如,如果您携带缓冲区的长度,您可以检查确保没有代码试图读取超过缓冲区的末尾,并且仍然为调用程序保留一行代码。

当然,您可以在一行中完成。但我会质疑为什么,因为当代码被塞进更少的行时,它的可读性总是会降低。编译器并不关心您是在一个或多个中执行此操作。

我认为,最可读的一句话是每次递增:

fooStruct.M1 = (*bufPointer++ << 8) | *bufPointer++;

但有些人可能会对此表示反对

[注意,根据下面的评论,以上内容已删除。我的建议很糟糕!其他答案包含避免序列点问题的一行代码]

我个人的编码风格是明确我正在寻址一个记录并使用它的片段:

fooStruct.M1 = (bufPointer[0] << 8) | bufPointer[1];
bufPointer += 2;

但这当然是两条线。。。任何称职的编译器都应该为这两种变体生成相同的程序集。


只是事后编辑。。。从你的答案中的字里行间,我猜你希望这个代码是紧凑的,因为你正在分配单独的结构元素,而不能在循环中这样做。在这种情况下:

fooStruct.M1 = (bufPointer[0] << 8) | bufPointer[1];
fooStruct.M2 = (bufPointer[2] << 8) | bufPointer[3];
fooStruct.M3 = (bufPointer[4] << 8) | bufPointer[5];
fooStruct.M4 = (bufPointer[6] << 8) | bufPointer[7];
bufPointer += 8;  // Only if you need to keep deserialising

接受答案的微小变化:

c = (p[0] << 8) | p[1];

如果你在一台big-endian机器上,你的数据在16位边界对齐,或者支持未对齐的访问,那么你可以简单地:

c = *(short*)p;