我有一个函数,它读取无符号long-long中的每一位,并将值存储在数组中,现在我想将这个函数转换为返回数字中下一位的函数,并将数据处理留给调用方.
理想情况下,它的工作方式类似于getc(),我可以在其中执行以下操作:
int n;
while((n = getbit()) != NULL)
...
我知道静态索引变量是一个选项,但这不是很优雅.有更实用的方法吗?
我有一个函数,它读取无符号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
是最高阶位.在这两种情况下,ch
和mask
都是零初始化的.我们可以依赖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.
注意,上述逻辑明确依赖于mask
是unsigned 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