我有一个函数,它读取无符号long-long中的每一位,并将值存储在数组中,现在我想将这个函数转换为返回数字中下一位的函数,并将数据处理留给调用方.

理想情况下,它的工作方式类似于getc(),我可以在其中执行以下操作:

int n;
while((n = getbit()) != NULL)
    ...

我知道静态索引变量是一个选项,但这不是很优雅.有更实用的方法吗?

推荐答案

就像FILE struct 围绕输入包装各种状态一样,例如缓冲、错误状态etc,您可以用一个简单的 struct 包装提取位所需的状态:

// Struct used to manage reading bits from a file
struct bitstream
{
    FILE *fp;
    unsigned char ch;     // current char
    unsigned char mask;   // next bit mask
    unsigned char mask0;  // first bit mask
};

这将保持从中读取的FILE,跟踪从该文件中读取的最后一个字符,并管理一个位掩码,该掩码将一次 Select 一个位.由于不同的应用程序可能需要特定的bit order(LSB->;MSB或MSB->;LSB),因此会使用一个额外的成员mask0.稍后您将看到这是如何使用的.

首先,您需要一个函数来设置这个 struct ,为操作做好准备.我们有两个函数,它们显式地 Select 所需的位顺序:

// Initialize a bitstream where bit ordering is LSB -> MSB
void bitstream_init_lsb(struct bitstream* bs, FILE *fp)
{
    struct bitstream bs_init = { fp };
    bs_init.mask0 = 1u;
    *bs = bs_init;
}

// Initialize a bitstream where bit ordering is MSB -> LSB
void bitstream_init_msb(struct bitstream* bs, FILE *fp)
{
    struct bitstream bs_init = { fp };
    bs_init.mask0 = 1u << (CHAR_BIT - 1);
    *bs = bs_init;
}

注意这两个函数中mask0的差异.对于LSB优先级,mask0是最低阶位.对于MSB优先级,mask0是最高阶位.在这两种情况下,chmask都是零初始化的.我们可以依赖mask的零值来指示ch中的所有位都已读取,并且我们必须从文件中读取下一个字节.

The way this will work is that every time you read a bit, you shift the mask in the appropriate direction (left or right, depending on selected ordering). In either case, the mask will become zero again when the bit is shifted beyond its storage capabilities. You then know that it's time to read the next byte.

注意,上述逻辑明确依赖于maskunsigned char类型.这很重要.

// Get next bit (0 or 1) from bitstream, or EOF
int bitstream_get(struct bitstream *bs)
{
    // Get next character when next bit is zero
    if (bs->mask == 0) {
        int ch = fgetc(bs->fp);
        if (ch == EOF) {
            return EOF;
        }
        bs->ch = (unsigned char) ch;
        bs->mask = bs->mask0;
    }

    // Get next bit
    int result = (bs->ch & bs->mask) ? 1 : 0;
    if (bs->mask0 == 1u) {
        bs->mask <<= 1;
    } else {
        bs->mask >>= 1;
    }
    return result;
}

感谢chux - Reinstate Monica对我最初答案的建议improvements.以上是一个修订版,包含了这些更改,并明确实现了位排序.

要使用它,只需使用有效的FILE*初始化,然后像使用fgetc一样执行读取循环:

int bit;
struct bitstream bs;

bitstream_init_msb(&bs, stdin);

while (EOF != (bit = bitstream_get(&bs)))
{
    // ...
}

这是一个现场演示:https://godbolt.org/z/KdEMoTavh

C++相关问答推荐

如何将FileFilter添加到FileDialog GTK 4

POSIX文件描述符位置

标准的C17标准是用括号将参数包装在函数声明中吗

为什么在函数内部分配内存空间时需要添加符号?

在c++中使用堆栈的有效括号

我的程序在收到SIGUSR1信号以从PAUSE()继续程序时总是崩溃()

Setenv在c编程中的用法?

不同出处的指针可以相等吗?

<;unistd.h>;和<;sys/unistd.h>;之间有什么区别?

C语言中的外部关键字

运行时错误:在索引数组时加载类型为';char';`的空指针

C程序向服务器发送TCPRST

Linux分段故障(核心转储)

将非连续物理内存映射到用户空间

C编译和运行

发送和接收的消息中的Unix域套接字不匹配

为什么会出现此错误?二进制表达式的操作数无效

共享目标代码似乎不能在Linux上的进程之间共享

我可以使用Windows SDK';s IN6_IS_ADDR_LOOPBACK等,尽管没有文档?

访问未对齐联合的成员是否为未定义行为,即使被访问的成员已充分对齐?