【问题标题】:Zero-padding array in C++C ++中的零填充数组
【发布时间】:2020-03-21 15:47:48
【问题描述】:

我有一个 uint8_t 数组,包含三个字节。

// 1010 1010 1011 1011 1000 0000
uint8_t command[3] = {0xAA, 0xBB, 0x80};

我现在想在左侧用 7 个零填充数组,最终创建以下数组(我不关心第 4 个字节):

// 0000 0001 0101 0101 0111 0111 0000 0000
uint8_t expected_output[4] = {0x01, 0x55, 0x77, 0x00};

在 C++ 中实现这种零填充的最佳方法是什么?

【问题讨论】:

  • 看看std::bitset。
  • c is not c++ and c++ is not c,我去掉了标签
  • 谢谢@Evg,我会看看文档。我只想要一种不依赖于小/大端架构的可靠方式。
  • 如果字节序是一个问题,你到底想做什么?如果您仅在内部使用该数组,那么我会使用一个大的 uint32_t 来代替。任何字节顺序问题都消失了。如果你需要通过网络传输,数组就可以了,你有四个单字节,所以字节序没有问题,你只需要按正确的顺序放置它们。如果你想为 both 使用值/数组,那么你有矛盾的要求,你 将 必须以某种方式处理字节序(htole32 或 ntohl 等函数把它藏起来……)。
  • A uint8_t [4] 将始终依赖字节序,因为它将始终存储来自 LS 0x01, 0x55, 0x77, 0x00 MS 的数据。这与 Big Endian 32 位数一致。但是,如果您希望它等于 32 位数字 0x01557700,那么它就不再一致并且不会在 Little Endian 机器上工作。

标签: c++ arrays embedded padding


【解决方案1】:

使用std::bitset的解决方案:

std::array<std::uint8_t, 4>
left_pad(const std::array<std::uint8_t, 3>& in) {
    using Bitset = std::bitset<8 * 4>;
    Bitset bitset;

    for (auto b = in.begin(); b != in.end(); ++b, bitset <<= 8)
        bitset |= Bitset{*b};
    bitset >>= 7;

    std::array<std::uint8_t, 4> out;
    for (auto b = out.rbegin(); b != out.rend(); ++b, bitset >>= 8)
        *b = (bitset & Bitset{0xFF}).to_ulong();

    return out;
}

然后:

void foo(std::uint8_t[4]);

void bar() {
    std::array<std::uint8_t, 3> command{0xCA, 0xBB, 0x80};
    std::array<std::uint8_t, 4> out = left_pad(command);
    foo(out.data());
}

如果输入长度固定为 3 个字节并且不需要对更大长度进行泛化,则可以将 std::bitset&lt;8 * 4&gt; 替换为 std::uint32_t。

cmets讨论后补充。

此等效版本

std::array<std::uint8_t, 4>
left_pad2(const std::array<std::uint8_t, 3>& in) {
    using Bitset = std::bitset<8 * 4>;
    Bitset bitset;

    for (auto b = in.begin(); b != in.end(); ++b) {
        bitset <<= 8;
        bitset |= Bitset{*b};
    }
    bitset <<= 1;

    std::array<std::uint8_t, 4> out;
    for (auto b = out.rbegin(); b != out.rend(); ++b, bitset >>= 8)
        *b = (bitset & Bitset{0xFF}).to_ulong();

    return out;
}

为better assembly code 提供GCC 和-O3 优化:

left_pad2(std::array<unsigned char, 3ul> const&):
        movzx   eax, BYTE PTR [rdi]
        movzx   edx, BYTE PTR [rdi+1]
        sal     rax, 8
        or      rax, rdx
        movzx   edx, BYTE PTR [rdi+2]
        sal     rax, 8
        or      rax, rdx
        add     rax, rax
        bswap   eax
        ret

对

left_pad(std::array<unsigned char, 3ul> const&):
        movzx   eax, BYTE PTR [rdi]
        movzx   edx, BYTE PTR [rdi+1]
        sal     rax, 8
        or      rax, rdx
        movzx   edx, BYTE PTR [rdi+2]
        sal     rax, 8
        or      rax, rdx
        sal     rax, 8
        mov     rdx, rax
        shr     rdx, 7
        mov     BYTE PTR [rsp-1], dl
        mov     rdx, rax
        shr     rdx, 15
        mov     BYTE PTR [rsp-2], dl
        mov     rdx, rax
        shr     rax, 31
        shr     rdx, 23
        mov     BYTE PTR [rsp-4], al
        mov     BYTE PTR [rsp-3], dl
        mov     eax, DWORD PTR [rsp-4]
        ret

【讨论】:

  • 感谢您的解决方案。我只是尝试通过代码进行调试。插入命令 = 0b0000.0000.1111.1111.1000.0000 (0x00 0xFF 0x80) 时,我得到输出 0b0000.0000.0000.0001.1111.1111.0000.0000(0x00 0x01 0xFF 0x00)。我希望最后一个字节是 0x80 而不是 0x00(MSB 中的 1)。
  • @Lexusminds,不清楚为什么应该是0x80。 {0x12, 0x34, 0x56} 的预期输出是什么?
  • 也许我应该谈谈根本原因:我正在使用一个库来计算 17 位的 CRC-7。这 17 位由 8 个数据位、8 个命令位和一个常量“1”组成。由于该库将 char 数组作为其输入,因此我需要将 17 位转换为一个零填充的 24 位(3 字节)数组。因此,保留所有 17 位(包括“1”)至关重要。
  • 哦,你是对的 - 输出符合预期。对不起 - 午餐后不应该立即进行按位计算。
  • 运行时应该不需要将初始值写入bitset。
猜你喜欢
  • 2021-03-24
  • 2016-11-06
  • 1970-01-01
  • 2018-10-05
  • 2017-10-24
  • 1970-01-01
  • 2021-11-16
  • 1970-01-01
  • 2021-11-25
相关资源
最近更新 更多