返回具有uint16_t值的偏移量

Returning an offset with uint16_t values

本文关键字:偏移量 uint16 返回      更新时间:2023-10-16

我在尝试重新解释数据以从消息中提取信息时遇到了一些困难。 我试图在这里重现问题。

我通过将它们从堆栈中弹出来接收一系列长整数(32 位)。 我需要将它们组装成 4 字(16 字节)数据包。我在下面重新创建的结构类似于给定数据包的第一个单词。 我遇到的困难在于,为了确定哪个单词是起始数据包,以及哪种类型的数据包是哪种类型的数据包,我需要能够读取结构的 s5 成员中数据的八进制值。
简而言之,对于每条消息,我需要将位 16-31 解释为 16 位整数,无论它是否跨越其他消息的位边界。

我本以为这将是一项容易得多的任务,但我似乎无法让它工作。 这是我尝试过的。我只是得到空值。

struct S
{
    uint8_t s1  :8;
    short s2    :2;
    bool s3 :1;
    int s4  :5;
    uint16_t s5 :16;
};
int main() {
    S s;
    s.s1 = 3;
    s.s2 = 2;
    s.s3 = true;
    s.s4 = 1;
    s.s5 = 02050;
    long l;
    memcpy(&l, &s, sizeof(S));
    std::deque<long> d;
    d.push_back(l);
    cout << *((uint16_t*)(&d.front()+2)) <<endl;

如果您已经从流中获得了long值,为什么不直接使用位移呢?

假设数据是大端序的,你可以移开前 16 位,得到你的八进制值:

// 69733891 is the big-endian integral value represented by
// your posted sample data, so the octal value should be 02050,
// or as an int 1064
long l = 69733891; 
uint16_t s5 = l >> 16; // shift off to get the high value (s5)

对于小端序(如您的帖子中所暗示的那样),您可以使用按位 AND:

uint16_t s5 = l & 0xFFFF;

为了快速比较为位移生成的程序集与指针别名,以下是 GCC 生成的内容(无优化):

为位移生成的程序集(请注意,SAR是执行右位移的单个指令):

' uint16_t s5 = l >> 16;
mov    rax,QWORD PTR [rbp-0x18]
sar    rax,0x10
mov    WORD PTR [rbp-0x1a],ax

为指针别名生成的程序集:

' uint16_t s5 = *((uint16_t*)&d[0]);
lea    rax,[rbp-0x20]
mov    esi,0x0
mov    rdi,rax
call   4e <main+0x4e>
movzx  eax,WORD PTR [rax] ' this is the "4e" address called
mov    WORD PTR [rbp-0x12],ax

希望能有所帮助。

您在这里面临多个问题:

  • 位域打包是实现定义的
  • long重新解释为S*S&违反了严格的别名规则

如果您坚持使用long值,则必须使用有关编译器的假设,例如字节序,位打包顺序等,或者可能禁用字符串别名(我不建议这样做)。

溶液

如果l是由struct S实例的memcpy创建的,则将值复制回struct S的不同实例应得到完全相同的位布局。

因此,我们可以将deque内的前端对象复制到struct S的实例中,并检查它是否s5成员:

long f = d.front();
S sf;
memcpy(&sf, &f, sizeof(sf));
std::cout << std::oct << sf.s5 << std::endl;