【问题标题】:Little Big Endian Bit Operation and ExtractionLittle Big Endian 位运算与提取
【发布时间】:2014-05-25 23:53:21
【问题描述】:

我正在尝试解决驱动程序问题,我必须从任意位置开始的任意位流中提取多个位。搜了一下这附近的帖子,发现涉及的Bit操作大部分都是64位机或者没有特殊的存储布局条件。

嗯,Bit 流作为 Byte 数组存储在 32 位 Micro 中。但是流是小端的,例如下面的字节流

LSBit (Bit 0) --> 0100 1001 0100 0100

在内存中存储为

字节 0 1001 0010 位布局,位 7-> 1001 0010 -> 位 0

字节 1 0010 0010 位布局,位 15-> 0010 0010 -> 位 8

这里字节中的位布局是大端,但字节是小端。

如果我必须从 Little endian 流中提取 Bit 4 到 11 以获得 0010 1001,那么我需要提取

从字节 0 开始

1001,字节 0 的高半字节

从字节 1

0010,字节 1 的低半字节

将字节 1 的左移位得到 0010 并与字节 0 的 1001 进行或运算

问题是流可以达到 64 位的长度,并且位数(最多 64 个)是任意的,起始位也是任意的。但是对于内部存储,我当然可以存储在适合这些位的适当数据类型中。

除此之外,我必须以同样的方式将它们从有效数据打包到这个 Little Big Endian 流中。 :'(

我还必须担心运行时间以及 long 是 4 个字节的事实。 因此,要存储 48 位的值,我需要维护一个以主机格式排列的 6 字节数组。 没有编译器long long 支持

编辑:有 64 位 long long 支持;我刚刚检查了编译器手册。

请有任何建议,我现在被困了三个星期。

【问题讨论】:

  • 欢迎来到 Stack Overflow。请尽快阅读About 页面。我是否正确理解您有一个以小端顺序输入的字节流,并且该字节流最长可达 8 个字节。您必须从该流中获取任意一组位并将它们存储到一个 64 位整数中(因为最多可以请求 64 位)?
  • 这看起来像一个家庭作业...
  • @Joce Care 详细说明?...
  • 我认为小端和大端是指单词中的字节顺序。字节数组没有字节顺序,字节也没有真正的位顺序,它们是最小的可访问内存单元。如果您在一个字节中以相反的顺序获得位,请谷歌反转位。如果您需要在字节数组中调整字节顺序,请重新排序字节。分开操作。
  • 感谢您的问候。我现在正在阅读关于页面。是的,传入的流是小端的,并且以它到达的方式存储在内存中。这最终导致字节为 Little Endian,但字节中的位布局为 Big Endian :\。流最多可以是 64 位,没错,但起始位可以是任意的,位数也可以是任意的。而且 long 在 32 位 micro 上也是 4 个字节,因为编译器最大原始类型是 4 个字节。

标签: c++ c bitarray


【解决方案1】:

逐字节读取您的流,以主机字节顺序构建一个 64 位数字(因为这是最大比特流长度)。
然后使用标准位摆弄提取。

这个两步配方的好处是与主机端无关。

在索引i处获取n位的窗口:

uint64_t extract(uint64_t in, int n, int i) {
    return in<<(64-i-n)>>(64-n);
}

【讨论】:

  • 听起来是个好主意,但问题在于 WindRiver 编译器在 PPC 的 32 位机器上只有 4 个字节。因此,如果我处理 48 位长值,那么这是一个问题。嗯需要考虑。我一个字节一个字节地接近它,:D。从整个流的角度来看似乎更好地处理它。对于大于 32 位的值,我需要以主机格式存储它,但字节数比原始 long sizeof() 值更多。
  • long long --> uint64_t 在那里。现在我必须读取完整的流并转换为主机格式,然后对不属于数据的高有效位进行一些屏蔽,并右移不需要的位。
  • 我要避免的一件事是太多循环试图移出位,而是构建一个掩码来移出
  • 我想直接构建掩码,而不需要循环太多以移出不需要的更高位。
  • 注意:注意64-i-n64-n 的值应超出063 的范围,因为那是UB。
【解决方案2】:

我提供的解决方案是:

pickbits.h

#ifndef PICKBITS_H_INCLUDED
#define PICKBITS_H_INCLUDED

#include <stddef.h>
#include <stdint.h>

extern uint64_t pick_bits(unsigned char *bytes, size_t nbytes, int lo, int hi);

#endif /* PICKBITS_H_INCLUDED */

标头对于来自&lt;stddef.h&gt;size_t 和来自&lt;inttypes.h&gt;uint64_t 是必需的。重要的是,标头应该是独立的和幂等的。包含这两个标题是使pickbits.h 独立的必要条件;标头保护使其具有幂等性,尽管它们可以被删除,但仍然可以,因为代码中没有类型定义(并且标准标头已经被 C 标准限制为幂等)。

pickbits.c

#include "pickbits.h"
#include <assert.h>

uint64_t pick_bits(unsigned char *bytes, size_t nbytes, int lo, int hi)
{
  assert(bytes != 0 && nbytes > 0 && nbytes <= 8);
  assert(lo >= 0 && lo < 64);
  assert(hi >= 0 && hi < 64 && hi >= lo);
  uint64_t result = 0;
  for (int i = nbytes - 1; i >= 0; i--)
    result = (result << 8) | bytes[i];
  result >>= lo;
  result &= (UINT64_C(1) << (hi - lo + 1)) - 1;
  return result;
}

请注意,包含"pickbits.h" 首先检查标头是否是自包含的。 UINT64_C 宏确保常量 1 被视为 uint64_t 值。

picktest.c

#include "pickbits.h"
#include <inttypes.h>
#include <stdio.h>

int main(void)
{
  unsigned char d1[8] = "\xA5\xB4\xC3\xD2\xE1\xF0\x96\x87";
  /* Selecting nybbles */
  for (int u = 0; u < 64; u += 4)
  {
    uint64_t v = pick_bits(d1, sizeof(d1), u, u+3);
    printf("Picking bits %2d..%2d gives 0x%" PRIX64 "\n", u, u+3, v);
  }
  /* Select across nybble boundaries - T.B.D */
  return 0;
}

需要扩展测试以跨越 nybble 边界,或完全保持在 nybble 内。

样品运行

Picking bits  0.. 3 gives 0x5
Picking bits  4.. 7 gives 0xA
Picking bits  8..11 gives 0x4
Picking bits 12..15 gives 0xB
Picking bits 16..19 gives 0x3
Picking bits 20..23 gives 0xC
Picking bits 24..27 gives 0x2
Picking bits 28..31 gives 0xD
Picking bits 32..35 gives 0x1
Picking bits 36..39 gives 0xE
Picking bits 40..43 gives 0x0
Picking bits 44..47 gives 0xF
Picking bits 48..51 gives 0x6
Picking bits 52..55 gives 0x9
Picking bits 56..59 gives 0x7
Picking bits 60..63 gives 0x8

为什么不只有一个源文件?

答案:见Why is GCC 4.8.2 complaining about addition under strict overflow?

基本上,上面显示的代码在严格的警告下编译干净:

$ gcc -g -O3 -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
>     -Wold-style-definition -Wold-style-declaration -Werror -c \
>     pickbits.c picktest.c
$

如果集成到单个文件中,GCC 4.8.2 的“功能”会启动并生成警告(-Werror 转换为错误),即使它警告的情况不可能发生(溢出不能'如果类型是int8_t,则不会发生,更不用说int)。

【讨论】:

  • 我将这个 pick_bits() 与上面 deduplicator 提到的步骤结合起来。
  • 感谢您为此付出的努力,几个月来我一直拥有稳定的工作代码。工作无问题。
猜你喜欢
  • 2020-12-01
  • 2019-06-30
  • 2017-06-14
  • 2018-05-15
  • 2022-06-10
  • 1970-01-01
  • 2012-10-09
  • 1970-01-01
  • 2010-10-16
相关资源
最近更新 更多