我的问题可能看起来有点业余,但我在网上找不到答案。不久前,Minecraft的一位开发人员谈到了门在游戏中的存储方式(链接(。他说它们是使用4位存储的。2 表示方向,1 表示上部或下部,1 表示打开或关闭。这让我想到你是怎么做到的。你看到存储一个字节,因为一个ASCII字符是一个字节。但是,您不能只将某些字符写入只有位的文件中,因为字符不能仅存储一位,它们存储为字节。我知道有一点表示为 1 或 0。但是,将 1 或 0 写入文件将为您提供 4 个字节而不是一个位。那么这是怎么做到的呢?
谢谢。
这很可能是通过组合不同的二进制标志来完成的。为了能够给你一个清晰的解释,你必须了解数字是如何按位表示的。基本上,这就像十进制计数一样,唯一的区别是只有两个数字可用。下面是一个示例(十进制数 0-16 的二进制表示形式(。
Decimal Binary
0 0
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
9 1001
10 1010
11 1011
12 1100
13 1101
14 1110
15 1111
16 10000
这正是您的计算机存储数字的方式。您还必须了解按位运算符的基本特征。假设您有两行 8 位,如下所示:
10101010
01011111
如果使用按位或运算符 - "|" 组合这两行,则输出的位 x 为 1,如果行 1 的位 x 为 1 或行 2 的位 x 为 1。因此,在我们的例子中,结果将如下所示:
11111111
按位和运算符 (&( 检查第 1 行的位 x 是否为 1,第 2 行的位 x 是否为 1。在我们的例子中,这是输出:
00001010
按位异或(独占或(运算符 (^( 检查是否只有一行的位 x 为 1。我们的输出如下所示:
11110101
基本上,Minecraft的开发人员所做的是他们定义了一些标志。
// this is just an example, not Minecraft's actual code
public static final byte ORIENTATION_SOUTH = 0; // 00000000
public static final byte ORIENTATION_NORTH = 1; // 00000001
public static final byte ORIENTATION_WEST = 2; // 00000010
public static final byte ORIENTATION_EAST = 3; // 00000011
public static final byte PART_UPPER = 4; // 00000100 - third bit is 1
public static final byte PART_LOWER = 0; // 00000000 - third bit is 0
public static final byte STATE_OPEN = 8; // 00001000 - fourth bit is 1
public static fianl byte STATE_CLOSED = 0; // 00000000 - fourth bit is 0
因此,如果他们想要一个门的上部朝西并且是打开的门状态,他们会这样写:
byte state = ORIENTATION_WEST | PART_UPPER | STATE_OPEN;
按位计算,它看起来像这样:
00000010 | (orientation)
00000100 | (part)
00001000 (open or closed)
然后最终会变成:
00001110 (west, upper part, open)
虽然只使用字节的前 4 位,但正如你所说,你不能只保存位。很可能整个字节都被保存了。
你是对的,当你只需要存储一个位时,你不能这样做:你必须至少保存一个字节。
但是,如果您有多个项目,每个项目占用的时间少于一个字节,则可以将多个项目打包在一个字节中。
以 4 位门为例,假设您要存储有关两个门的信息。为此,您需要八位,这是一个字节。您可以将第一个门保存在下面的四个位中,将第二个门保存在上面的四个位中。当您需要获取第一扇门的状态时,通过应用twoDoorsInOneByte & 0x0F
操作来屏蔽第二扇门:它剥离了字节的较高部分。当你需要第二个门时,使用左移将其位移动到位:(twoDoorsInOneByte >> 4) & 0x0F
:它将上面的四个位移动到上面的四个位,并清除字节上半部分的位,以防你的值被叹息扩展。
最后,在 C 中,您还有另一种选择:可以使用位字段。当您想要将任意长度的元素打包成几个单词以节省空间时,这可能很有用。请注意,当节省相对较高或内存太稀少(例如,您正在为 8 位微控制器编写嵌入式代码(时,应谨慎应用此技术。
定义:
DOOR_UP = 0x01
DOOR_LEFT = 0x02
DOOR_BIG = 0x04
DOOR_SMALL = 0x08
DOOR_RED = 0x10
DOOR_BLUE = 0x20
DOOR_GREEN = 0x40
如果你需要左,小,绿色的门写
def door := DOOR_LEFT or DOOR_SMALL or DOOR_GREEN (Z** or some generic language)
byte door = DOOR_LEFT | DOOR_SMALL | DOOR_GREEN; (C++)
要检查的是门绿色"问":
door & DOOR_GREEN
零表示不绿色。非零(实际上是DOOR_GREEN(表示绿门
加法:
您甚至可以在一个字节中打包 0-7 中的两个数字。ZX Spectrum(1982(中也使用了同样的想法来定义字符的颜色:
Xbbbfff (bbb - background color, fff - foreground color, X - for blink)
要将 bkg 颜色 5 和 fgd 设置为 4,请使用:
color = 5 << 3 | 4
要获取背景颜色,请使用:
( color >> 3 ) & mask(0x7) -> 5
如果你做游戏,每一个nsec都很重要。尽量避免使用位操作,只要有可能:)
虽然他们说他们使用的是 4 位,但我知道他们实际上是将信息存储在一个字节中(这很容易创建,因为字节是一种原始数据类型(。
我不相信可以直接存储一个位,但我想你想知道,虽然他们说他们使用的是 4 位,但这些位存储在一个字节中。
几乎所有现代计算机都是字节可寻址的,这意味着您只能寻址(指向(一个字节的内存,而不能更小。因此,在这种情况下,他们可能存储了一个字节。为了获得他们关心的位,他们使用位操作来操作字节:
const int OPEN_CLOSE_BIT = 1 << 0;
const int UPPER_LOWER_BIT = 1 << 1;
byte doorByte = getByteFromSomewhere();
if (doorByte & UPPER_LOWER_BIT) {
// the door is in some state
}