【问题标题】:Shift an array to the right with 1将数组右移 1
【发布时间】:2016-06-16 14:14:17
【问题描述】:

我正在尝试用一些二进制 1 将一个 unsigned char 数组向右移动。

示例:0000 0000 | 0000 1111 我换 8 次会给我 0000 1111 | 1111 1111(二进制左移)

所以在我的数组中我会得到:{0x0F, 0x00, 0x00, 0x00} => {0xFF, 0x0F, 0x00, 0x00}(数组中的右移)

我目前使用函数 memmove:

unsigned char * dataBuffer = {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

unsigned int shift = 4;
unsigned length = 8;

memmove(dataBuffer, dataBuffer - shift, length + shift);    
for(int i = 0 ; i < 8 ; i++) printf("0x%X ", dataBuffer[i]);

Output: 0x0 0x0 0x0 0x0 0xF 0x0 0x0 0x0
Expected output: 0xFF 0x0 0x0 0x0 0x0 0x0 0x0 0x0

如您所见,我设法仅逐个元素地移动我的数组,我不知道如何将 0 替换为 1。我想使用 memset 可以工作,但我无法正确使用它。

感谢您的帮助!

编辑:这是为了填充 exFAT 磁盘的位图区域。在磁盘中写入簇时,必须将位图的相应位设置为 1(第一个簇为第一位,第二个簇为第二位,...)。

新格式化的驱动器将在位图的第一个字节中包含 0x0F,因此如果我编写 8 个簇,建议的示例符合我的需要,我需要将值移动 8 次并用 1 填充。

在代码中,我写了 4 个簇,需要将值移动 4 位,但它移动了 4 个字节。

将问题设置为已解决,不可能做我想做的事。我需要分别移动数组的每个字节,而不是移动数组的位。

【问题讨论】:

  • 对于这类问题最好把问题说清楚。它们看起来像什么 - 输入和输出?究竟是什么预期的行为?你举了一个例子,但这真的能满足你的要求吗?你需要那个干什么?
  • 这是为了填充 exFAT 磁盘的位图区域。在磁盘中写入簇时,必须将位图的相应位设置为 1(第一个簇为第一位,第二个簇为第二位,...)。新格式化的驱动器将在位图的第一个字节中包含 0x0F,因此如果我编写 8 个集群,建议的示例符合我的需要,我需要将值移动 8 次并用 1 填充。
  • 移位在这里没有意义 - 首先,您选择写入哪些集群,这根本不涉及移位,只需设置与您正在编写的集群相对应的位(它们无论如何可能不连续)。其次,您的示例表明您正在向右移动,但使用 1 而不是 0 填充,这与正常位移相反。要进行位移,您可以使用 > 位级运算符来执行此操作,自己管理用于位移的任何字长的溢出。但是,根据您的描述,这听起来不像您真正想要的。
  • @ArthurPenguin 您能否先检查以下链接是否回答了您的问题? stackoverflow.com/a/47990/1566187

标签: c++ arrays binary shift


【解决方案1】:

将问题设置为已解决,不可能做我想做的事。我需要分别编辑数组的每一位,而不是移动数组的位。

如果它可以帮助其他人,这是代码:

unsigned char dataBuffer[11] = {0x0F, 0x00, 0x00, 0x00, 0, 0, 0, 0};
unsigned int sizeCluster = 6;
unsigned int firstCluster = 4;
unsigned int bitIndex = firstCluster % 8;
unsigned int byteIndex = firstCluster / 8;
for(int i = 0 ; i < sizeCluster; i++){
    dataBuffer[byteIndex] |= 1 << bitIndex;
    //printf("%d ", bitIndex);
    //printf("%d \n\r", byteIndex);
    bitIndex++;
    if(bitIndex % 8 == 0){
        bitIndex = 0;
        byteIndex++;
    }
}
for(int i = 0 ; i < 10 ; i++) printf("0x%X ", dataBuffer[i]);

OUTPUT: 0xFF 0x3 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 

sizeCluster 是我想在 Bitmap 中添加的簇数 firstCluster 是我可以写入数据的第一个集群(使用了 4 个集群:0、1、2 和 3,所以我从 4 开始)。 bitIndex 用于修改数组字节中的正确位 => 每次递增。 byteIndex 用于修改数组的右字节 => 每次该位等于 7 时递增。

【讨论】:

  • 你没有移动数组,你只是为特定的集群设置特定的位,这是经典的位掩码集。问题是,为什么你不使用 C++ bitset
  • 大多数时候(实际上总是)我将数组作为字节而不是位来操作。在我的概念中,它仅对位图是必需的,所以我从未考虑过将其转换为位(直到现在我使用的是 FAT32)。不知道bitset功能,你觉得会更优化吗?我用来读取/写入数据的 sata 函数使用无符号字符缓冲区,因此我需要将它们转换回字节。
【解决方案2】:

如果您出于性能原因不想使用 C++ std::bitset,那么您的代码可以这样重写:

#include <cstdio>
#include <cstdint>

    // buffer definition
    constexpr size_t clustersTotal = 83;
    constexpr size_t clustersTotalBytes = (clustersTotal+7)>>3; //ceiling(n/8)
    uint8_t clustersSet[clustersTotalBytes] = {0x07, 0};
        // clusters 0,1 and 2 are already set (for show of)

    // helper constanst bit masks for faster bit setting
    // could be extended to uint64_t and array of qwords on 64b architectures
    // but I couldn't be bothered to write all masks by hand.
    // also I wonder when the these lookup tables would be large enough
    // to disturb cache locality, so shifting in code would be faster.
    const uint8_t bitmaskStarting[8] = {0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80};
    const uint8_t bitmaskEnding[8] = {0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};
    constexpr uint8_t bitmaskFull = 0xFF;

    // Input values
    size_t firstCluster = 6;
    size_t sizeCluster = 16;

    // set bits (like "void setBits(size_t firstIndex, size_t count);" )
    auto lastCluster = firstCluster + sizeCluster - 1;
    printf("From cluster %d, size %d => last cluster is %d\n",
           firstCluster, sizeCluster, lastCluster);
    if (0 == sizeCluster || clustersTotal <= lastCluster)
        return 1;     // Invalid input values
    auto firstClusterByte = firstCluster>>3; // div 8
    auto firstClusterBit = firstCluster&7;   // remainder
    auto lastClusterByte = lastCluster>>3;
    auto lastClusterBit = lastCluster&7;
    if (firstClusterByte < lastClusterByte) {
        // Set the first byte of sequence (by mask from lookup table (LUT))
        clustersSet[firstClusterByte] |= bitmaskStarting[firstClusterBit];
        // Set bytes between first and last (simple 0xFF - all bits set)
        while (++firstClusterByte < lastClusterByte)
            clustersSet[firstClusterByte] = bitmaskFull;
        // Set the last byte of sequence (by mask from ending LUT)
        clustersSet[lastClusterByte] |= bitmaskEnding[lastClusterBit];
    } else {    //firstClusterByte == lastClusterByte special case
        // Intersection of starting/ending LUT masks is set
        clustersSet[firstClusterByte] |=
            bitmaskStarting[firstClusterBit] & bitmaskEnding[lastClusterBit];
    }

    for(auto i = 0 ; i < clustersTotalBytes; ++i)
        printf("0x%X ", clustersSet[i]); // Your debug display of buffer

不幸的是,我没有分析任何版本(你的和我的),所以我不知道在这两种情况下优化的 C 编译器输出的质量如何。在蹩脚的 C 编译器和 386-586 处理器的时代,我的版本会快得多。使用现代 C 编译器,LUT 的使用可能会适得其反,但除非有人通过一些分析结果证明我错了,否则我仍然认为我的版本效率更高。

也就是说,由于在此之前可能涉及写入文件系统,因此即使使用您的变体,设置位也可能需要大约 %0.1 的 CPU 时间,I/O 等待将是主要因素。

所以我发布这个更像是一个如何以不同方式完成事情的示例。

编辑:

另外,如果您相信 clib 优化,那么:

        // Set bytes between first and last (simple 0xFF - all bits set)
        while (++firstClusterByte < lastClusterByte)
            clustersSet[firstClusterByte] = bitmaskFull;

可以复用clibmemsetmagic:

//#include <cstring>
    // Set bytes between first and last (simple 0xFF - all bits set)
    if (++firstClusterByte < lastClusterByte)
        memset(clustersSet, bitmaskFull, (lastClusterByte - firstClusterByte));

【讨论】:

    猜你喜欢
    • 2020-03-03
    • 2022-01-05
    • 2017-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多