C中位数组的结构
Structure for an array of bits in C
我注意到 C 中没有针对单个位的内置结构。有 (unsigned) char 和 int,它们是 8 位(一个字节),long 是 64 位以上,依此类推 (uint64_t, bool...)
我在编写霍夫曼树时遇到了这个问题,某些字符的编码不一定正好是 8 位长(例如 00101),因此没有有效的方法来存储编码。我必须找到临时解决方案,例如字符串或布尔数组,但这需要更多的内存。
但无论如何,我的问题更笼统:是否有存储 数组 位或某种用户定义结构的好方法?我在网上搜索了一个,但最小的结构似乎是 8 位(一个字节)。我试过 int a : 1
之类的东西,但没有用。我阅读了有关位字段的信息,但它们并不能完全实现我想要做的事情。我知道在 C++ 中已经有人问过这个问题,如果有一个位的结构,但主要是我想知道在 C 中存储诸如 00101 之类的编码的最有效内存方式是什么。
It has come to my attention that there is no builtin structure for a single bit in C.
这是真的,而且这是有道理的,因为基本上没有机器具有位可寻址内存。
But anyways, my question is more general: is there a good way to store
an array of bits, or some sort of user-defined struct?
通常使用 unsigned char
或其他无符号整数类型,或此类数组。除此之外,您还需要一些屏蔽和移位来设置或读取各个位的值。
I scoured the
web for one but the smallest structure seems to be 8 bits (one byte).
从技术上讲,最小的可寻址存储单元 ([[un
]signed
] char
) 可能大于 8 位,尽管您不太可能看到这一点。
I tried things such as int a : 1 but it didn't work. I read about bit
fields but they do not simply achieve exactly what I want to do.
位域只能作为结构成员出现。包含此类位域的结构对象的大小仍将是 char
大小的倍数,因此不能很好地映射到位数组或位数组的任何部分。
I
know questions have already been asked about this in C++ and if there
is a struct for a single bit, but mostly I want to know specifically
what would be the most memory-efficient way to store an encoding such
as 00101 in C.
如果您需要一个位模式 和 一个单独的位计数——例如如果位存储对象中的一些可用位实际上并不重要——那么你需要一个单独的有效位计数数据。如果您想要一个用于少量但可变位数的数据结构,那么您可能会采用以下方式:
struct bit_array_small {
unsigned char bits;
unsigned char num_bits;
};
当然,您可以通过为 bits
成员和 num_bits
成员选择不同的数据类型来使其更大。我敢肯定,如果您碰巧需要的话,您可以了解如何将这个概念扩展到处理任意长度的位数组。
如果你真的想要最大的内存效率,你可以将霍夫曼树本身编码为比特流。参见,例如:
https://www.siggraph.org/education/materials/HyperGraph/video/mpeg/mpegfaq/huffman_tutorial.html
然后将这些位编码为字节数组,可能会浪费 7 位。
但那将是一个可怕的想法。为了使内存中的结构有用,它必须易于访问。你仍然可以非常有效地做到这一点。假设您要对最多 12 位代码进行编码。使用 16 位整数和位域:
struct huffcode {
uint16_t length: 4,
value: 12;
}
C 会将其存储为单个 16 位值,并允许您分别访问长度和值字段。完整的 Huffman 节点还将包含输入代码值和树指针(如果您想要进一步紧凑,可以是数组中的整数索引)。
如果您主要对一次访问一个位感兴趣,您可以采用 unsigned char
的数组并将其视为位数组。例如:
unsigned char array[125];
假设每个字节有 8 位,这可以被视为 1000 位的数组。前 16 个在逻辑上是这样的:
---------------------------------------------------------------------------------
byte | 0 | 1 |
---------------------------------------------------------------------------------
bit | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---------------------------------------------------------------------------------
假设您要使用 bit b
。然后您可以执行以下操作:
读取位 b
:
value = (array[b/8] & (1 << (b%8)) != 0;
设置位 b
:
array[b/8] |= (1 << (b%8));
清除位 b
:
array[b/8] &= ~(1 << (b%8));
将位数除以 8 得到相关字节。类似地,mod 将位数加 8 会为您提供该字节内的相关位。然后,您将值 1 左移位数,得到必要的位掩码。
虽然这里有整数除法和 modulus,但被除数是 2 的幂,因此任何体面的编译器都应该用位 shifting/masking.
替换它们
您可以立即制作自己的位数组。
#define ba_set(ptr, bit) { (ptr)[(bit) >> 3] |= (char)(1 << ((bit) & 7)); }
#define ba_clear(ptr, bit) { (ptr)[(bit) >> 3] &= (char)(~(1 << ((bit) & 7))); }
#define ba_get(ptr, bit) ( ((ptr)[(bit) >> 3] & (char)(1 << ((bit) & 7)) ? 1 : 0 )
#define ba_setbit(ptr, bit, value) { if (value) { ba_set((ptr), (bit)) } else { ba_clear((ptr), (bit)); } }
#define BITARRAY_BITS (120)
int main()
{
char mybits[(BITARRAY_BITS + 7) / 8];
memset(mybits, 0, sizeof(mybits));
ba_setbit(mybits, 33, 1);
if (!ba_get(33))
return 1;
return 0;
};
我注意到 C 中没有针对单个位的内置结构。有 (unsigned) char 和 int,它们是 8 位(一个字节),long 是 64 位以上,依此类推 (uint64_t, bool...)
我在编写霍夫曼树时遇到了这个问题,某些字符的编码不一定正好是 8 位长(例如 00101),因此没有有效的方法来存储编码。我必须找到临时解决方案,例如字符串或布尔数组,但这需要更多的内存。
但无论如何,我的问题更笼统:是否有存储 数组 位或某种用户定义结构的好方法?我在网上搜索了一个,但最小的结构似乎是 8 位(一个字节)。我试过 int a : 1
之类的东西,但没有用。我阅读了有关位字段的信息,但它们并不能完全实现我想要做的事情。我知道在 C++ 中已经有人问过这个问题,如果有一个位的结构,但主要是我想知道在 C 中存储诸如 00101 之类的编码的最有效内存方式是什么。
It has come to my attention that there is no builtin structure for a single bit in C.
这是真的,而且这是有道理的,因为基本上没有机器具有位可寻址内存。
But anyways, my question is more general: is there a good way to store an array of bits, or some sort of user-defined struct?
通常使用 unsigned char
或其他无符号整数类型,或此类数组。除此之外,您还需要一些屏蔽和移位来设置或读取各个位的值。
I scoured the web for one but the smallest structure seems to be 8 bits (one byte).
从技术上讲,最小的可寻址存储单元 ([[un
]signed
] char
) 可能大于 8 位,尽管您不太可能看到这一点。
I tried things such as int a : 1 but it didn't work. I read about bit fields but they do not simply achieve exactly what I want to do.
位域只能作为结构成员出现。包含此类位域的结构对象的大小仍将是 char
大小的倍数,因此不能很好地映射到位数组或位数组的任何部分。
I know questions have already been asked about this in C++ and if there is a struct for a single bit, but mostly I want to know specifically what would be the most memory-efficient way to store an encoding such as 00101 in C.
如果您需要一个位模式 和 一个单独的位计数——例如如果位存储对象中的一些可用位实际上并不重要——那么你需要一个单独的有效位计数数据。如果您想要一个用于少量但可变位数的数据结构,那么您可能会采用以下方式:
struct bit_array_small {
unsigned char bits;
unsigned char num_bits;
};
当然,您可以通过为 bits
成员和 num_bits
成员选择不同的数据类型来使其更大。我敢肯定,如果您碰巧需要的话,您可以了解如何将这个概念扩展到处理任意长度的位数组。
如果你真的想要最大的内存效率,你可以将霍夫曼树本身编码为比特流。参见,例如:
https://www.siggraph.org/education/materials/HyperGraph/video/mpeg/mpegfaq/huffman_tutorial.html
然后将这些位编码为字节数组,可能会浪费 7 位。
但那将是一个可怕的想法。为了使内存中的结构有用,它必须易于访问。你仍然可以非常有效地做到这一点。假设您要对最多 12 位代码进行编码。使用 16 位整数和位域:
struct huffcode {
uint16_t length: 4,
value: 12;
}
C 会将其存储为单个 16 位值,并允许您分别访问长度和值字段。完整的 Huffman 节点还将包含输入代码值和树指针(如果您想要进一步紧凑,可以是数组中的整数索引)。
如果您主要对一次访问一个位感兴趣,您可以采用 unsigned char
的数组并将其视为位数组。例如:
unsigned char array[125];
假设每个字节有 8 位,这可以被视为 1000 位的数组。前 16 个在逻辑上是这样的:
---------------------------------------------------------------------------------
byte | 0 | 1 |
---------------------------------------------------------------------------------
bit | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---------------------------------------------------------------------------------
假设您要使用 bit b
。然后您可以执行以下操作:
读取位 b
:
value = (array[b/8] & (1 << (b%8)) != 0;
设置位 b
:
array[b/8] |= (1 << (b%8));
清除位 b
:
array[b/8] &= ~(1 << (b%8));
将位数除以 8 得到相关字节。类似地,mod 将位数加 8 会为您提供该字节内的相关位。然后,您将值 1 左移位数,得到必要的位掩码。
虽然这里有整数除法和 modulus,但被除数是 2 的幂,因此任何体面的编译器都应该用位 shifting/masking.
替换它们您可以立即制作自己的位数组。
#define ba_set(ptr, bit) { (ptr)[(bit) >> 3] |= (char)(1 << ((bit) & 7)); }
#define ba_clear(ptr, bit) { (ptr)[(bit) >> 3] &= (char)(~(1 << ((bit) & 7))); }
#define ba_get(ptr, bit) ( ((ptr)[(bit) >> 3] & (char)(1 << ((bit) & 7)) ? 1 : 0 )
#define ba_setbit(ptr, bit, value) { if (value) { ba_set((ptr), (bit)) } else { ba_clear((ptr), (bit)); } }
#define BITARRAY_BITS (120)
int main()
{
char mybits[(BITARRAY_BITS + 7) / 8];
memset(mybits, 0, sizeof(mybits));
ba_setbit(mybits, 33, 1);
if (!ba_get(33))
return 1;
return 0;
};