在C++中,为什么bool类型需要一个字节的存储空间来存储true或false,而一个bit就足够了,比如用0表示false,1表示true?(Java也为什么需要一个字节?)
另外,使用以下代码有多安全?
struct Bool {
bool trueOrFalse : 1;
};
第三,即使它是安全的,上述领域技术真的会有所帮助吗?因为我听说我们在那里节省空间,但仍然编译器生成的访问它们的代码比访问基本类型生成的代码更大、更慢。
在C++中,为什么bool类型需要一个字节的存储空间来存储true或false,而一个bit就足够了,比如用0表示false,1表示true?(Java也为什么需要一个字节?)
另外,使用以下代码有多安全?
struct Bool {
bool trueOrFalse : 1;
};
第三,即使它是安全的,上述领域技术真的会有所帮助吗?因为我听说我们在那里节省空间,但仍然编译器生成的访问它们的代码比访问基本类型生成的代码更大、更慢。
为什么bool需要一个字节来存储true或false,而一个位就足够了?
因为C++中的每个对象都必须是可寻址的*(也就是说,您必须能够有指向它的指针)。您无法访问单个位(至少不是在传统硬件上)。
使用以下内容有多安全?
它是“安全”的,但并没有实现太多。
上述字段技术真的会有所帮助吗?
不会,原因与上述相同;)
但是,编译器生成的代码访问它们比访问原语的代码更大且更慢。
是的,这是真的。在大多数平台上,这需要访问包含的字节(或int
或其他内容),然后执行位移和位掩码操作以访问相关位。
std::bitset
或在Java中使用BitSet
,它们可以打包位。
* 除了少数例外。
使用单个比特位速度慢且分配更加复杂。在C/C++中,无法获取一个比特位的地址,因此您无法将&trueOrFalse
用作比特位。
Java有BitSet和EnumSet两种使用位图的方法。如果您只有很少的数字,可能不会有太大区别。例如,对象必须至少按字节对齐,在HotSpot中是8字节对齐(在C++中,new
Object可以是8到16字节对齐)。这意味着节省几个比特位可能并不会节省任何空间。
至少在Java中,位操作并不比其他操作更快,除非它们更适合缓存。
public static void main(String... ignored) {
BitSet bits = new BitSet(4000);
byte[] bytes = new byte[4000];
short[] shorts = new short[4000];
int[] ints = new int[4000];
for (int i = 0; i < 100; i++) {
long bitTime = timeFlip(bits) + timeFlip(bits);
long bytesTime = timeFlip(bytes) + timeFlip(bytes);
long shortsTime = timeFlip(shorts) + timeFlip(shorts);
long intsTime = timeFlip(ints) + timeFlip(ints);
System.out.printf("Flip time bits %.1f ns, bytes %.1f, shorts %.1f, ints %.1f%n",
bitTime / 2.0 / bits.size(), bytesTime / 2.0 / bytes.length,
shortsTime / 2.0 / shorts.length, intsTime / 2.0 / ints.length);
}
}
private static long timeFlip(BitSet bits) {
long start = System.nanoTime();
for (int i = 0, len = bits.size(); i < len; i++)
bits.flip(i);
return System.nanoTime() - start;
}
private static long timeFlip(short[] shorts) {
long start = System.nanoTime();
for (int i = 0, len = shorts.length; i < len; i++)
shorts[i] ^= 1;
return System.nanoTime() - start;
}
private static long timeFlip(byte[] bytes) {
long start = System.nanoTime();
for (int i = 0, len = bytes.length; i < len; i++)
bytes[i] ^= 1;
return System.nanoTime() - start;
}
private static long timeFlip(int[] ints) {
long start = System.nanoTime();
for (int i = 0, len = ints.length; i < len; i++)
ints[i] ^= 1;
return System.nanoTime() - start;
}
打印
Flip time bits 5.0 ns, bytes 0.6, shorts 0.6, ints 0.6
对于大小为40000和400K的尺寸
Flip time bits 6.2 ns, bytes 0.7, shorts 0.8, ints 1.1
适用于 4M
Flip time bits 4.1 ns, bytes 0.5, shorts 1.0, ints 2.3
和40M
Flip time bits 6.2 ns, bytes 0.7, shorts 1.1, ints 2.4
char
更紧凑的了,它是 C/C++ 中最小的可寻址内存单元。(根据实现方式,bool
可能与 char
大小相同,但 允许更大。)char
至少可以容纳 8 个位,但也可以包含更多。确切的数量可以通过在 limits.h
(在 C 中)或 climits
(在 C++ 中)中定义的 CHAR_BIT
宏获得。今天,最常见的是 CHAR_BIT == 8
,但您不能依赖它(请参见 这里)。但是,在 POSIX 兼容系统和 Windows 上,保证为 8。
虽然无法减少单个标志的内存占用,但当然可以组合多个标志。除了手动执行所有位操作外,还有一些替代方案:
std::bitset
boost::dynamic_bitset
char
)的不同对象总是安全的。char
是 C/C++ 保证提供的最小类型。一些编译器可能提供更小的类型,有或没有限制。我记得有一种用于图形设计的芯片,其中所有地址都是位地址,因此将 char*
增加一个值需要将指针所表示的值加上 8。从未对齐的地址读取 char
会比从对齐的地址读取速度慢,但不需要任何额外的指令。此外,许多较小的微控制器具有高效的位测试/设置/清除指令,... - supercat为什么不将状态存储为字节?没有实际测试下面的代码,但是应该能给你一个想法。你甚至可以利用短整型或整型来表示16或32个状态。我相信我有一个可工作的JAVA示例。我找到它后会发布。
__int8 state = 0x0;
bool getState(int bit)
{
return (state & (1 << bit)) != 0x0;
}
void setAllOnline(bool online)
{
state = -online;
}
void reverseState(int bit)
{
state ^= (1 << bit);
}
好的,这是JAVA版本。我将其存储为Int值,因为如果我记得正确,即使使用byte也会使用4个字节。而且这显然不会被用作数组。
public class State
{
private int STATE;
public State() {
STATE = 0x0;
}
public State(int previous) {
STATE = previous;
}
/*
* @Usage - Used along side the #setMultiple(int, boolean);
* @Returns the value of a single bit.
*/
public static int valueOf(int bit)
{
return 1 << bit;
}
/*
* @Usage - Used along side the #setMultiple(int, boolean);
* @Returns the value of an array of bits.
*/
public static int valueOf(int... bits)
{
int value = 0x0;
for (int bit : bits)
value |= (1 << bit);
return value;
}
/*
* @Returns the value currently stored or the values of all 32 bits.
*/
public int getValue()
{
return STATE;
}
/*
* @Usage - Turns all bits online or offline.
* @Return - <TRUE> if all states are online. Otherwise <FALSE>.
*/
public boolean setAll(boolean online)
{
STATE = online ? -1 : 0;
return online;
}
/*
* @Usage - sets multiple bits at once to a specific state.
* @Warning - DO NOT SET BITS TO THIS! Use setMultiple(State.valueOf(#), boolean);
* @Return - <TRUE> if states were set to online. Otherwise <FALSE>.
*/
public boolean setMultiple(int value, boolean online)
{
STATE |= value;
if (!online)
STATE ^= value;
return online;
}
/*
* @Usage - sets a single bit to a specific state.
* @Return - <TRUE> if this bit was set to online. Otherwise <FALSE>.
*/
public boolean set(int bit, boolean online)
{
STATE |= (1 << bit);
if(!online)
STATE ^= (1 << bit);
return online;
}
/*
* @return = the new current state of this bit.
* @Usage = Good for situations that are reversed.
*/
public boolean reverse(int bit)
{
return (STATE ^= (1 << bit)) == (1 << bit);
}
/*
* @return = <TRUE> if this bit is online. Otherwise <FALSE>.
*/
public boolean online(int bit)
{
int value = 1 << bit;
return (STATE & value) == value;
}
/*
* @return = a String contains full debug information.
*/
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("TOTAL VALUE: ");
sb.append(STATE);
for (int i = 0; i < 0x20; i++)
{
sb.append("\nState(");
sb.append(i);
sb.append("): ");
sb.append(online(i));
sb.append(", ValueOf: ");
sb.append(State.valueOf(i));
}
return sb.toString();
}
}
还有一点需要指出的是,你真的不应该为此使用特殊类,而是将变量存储在最可能使用它的类中。如果你计划拥有数百甚至数千个布尔值,请考虑使用字节数组。
例如下面的示例:
boolean[] states = new boolean[4096];
int[] states = new int[128];
boolean getState(int index)
{
return (states[index >> 5] & 1 << (index & 0x1F)) != 0x0;
}
更多信息...
基本上,如果上面的内容有点混乱,这里是一个简化版本。
类型"byte"、"short"、"int"、"long"都是具有不同范围的数据类型。
您可以查看此链接:http://msdn.microsoft.com/en-us/library/s3f49ktz(v=vs.80).aspx,以查看每种类型的数据范围。
因此,一个字节等于8位。所以一个4字节的int将是32位。
现在没有任何简单的方法来执行某个值的N次幂。但是,由于位移,我们可以模拟它。通过执行1 << N,这相当于1 * 2^N。因此,如果我们做2 << 2^N,我们将做2 * 2^N。因此,要执行二的幂,请始终执行“1 << N”。
现在我们知道int将有32位,因此可以使用每个位,因此我们可以简单地对它们进行索引。
为了保持简单,将"&"运算符视为检查一个值是否包含另一个值的位的方法。因此,假设我们有一个值为31。要达到31。我们必须添加以下位0到4。它们是1、2、4、8和16。这些加起来等于31。现在,当我们执行31 & 16时,这将返回16,因为位4即2^4 = 16。位于该值中。现在假设我们执行31 & 20,这是检查位2和4是否位于该值中。这将返回20,因为位2和4都位于此处2^2 = 4 + 2^4 = 16 = 20。现在假设我们执行31 & 48。这是检查位4和5。好吧,我们在31中没有第5位。因此,这只会返回16。它不会返回0。因此,在执行多个检查时,必须检查它是否等于该值。而不是检查它是否等于0。
以下内容将验证单个位是否为0或1。0表示false,1表示true。
bool getState(int bit)
{
return (state & (1 << bit)) != 0x0;
}
int value = 31;
value |= 16;
value |= 16;
value |= 16;
value |= 16;
/*
* This contains bits 0,1,2,3,4,8,9 turned on.
*/
const int CHECK = 1 | 2 | 4 | 8 | 16 | 256 | 512;
/*
* This is some value were we add bits 0 through 9, but we skip 0 and 8.
*/
int value = 2 | 4 | 8 | 16 | 32 | 64 | 128 | 512;
int return_code = value & CHECK;
返回代码将为2 + 4 + 8 + 16 + 512 = 542。
所以我们正在检查799,但是我们收到了542。这是因为位o和8离线,我们等于256 + 1 = 257,799 - 257 = 542。
以上是检查是否按下某些按钮的绝佳方法,例如我们要制作一个视频游戏,并希望检查是否按下了某些按钮。我们可以使用一次检查来检查每个位,这比在每个状态上执行布尔检查更有效率。
现在假设我们有一个始终反转的布尔值。
通常你会做这样的事情:
bool state = false;
state = !state;
使用位运算符"^"也可以实现此操作。
就像我们执行"1 << N"来选择该位的整个值一样,我们也可以反过来做同样的事情。所以就像我们展示了"|="存储返回值一样,我们将使用"^="做同样的事情。所以这个操作的作用是,如果该位为开启状态,则关闭它;如果该位为关闭状态,则打开它。
void reverseState(int bit)
{
state ^= (1 << bit);
}
您甚至可以让它返回当前状态。如果您希望它返回先前的状态,只需将 "!=" 替换为 "== "。因此,这样做会执行反转,然后检查当前状态。
bool reverseAndGet(int bit)
{
return ((state ^= (1 << bit)) & (1 << bit)) != 0x0;
}
int posX = 0;
int posY = 0;
int posZ = 0;
int position = (x << 0) | (y << 10) | (z << 20);
return this.position == position;
return this.x == x && this.y == y && this.z == z;
int getX()
{
return position & 1023;
}
对于 y,我们需要执行左位移,然后进行 AND 操作。
int getY()
{
return (position >> 10) & 1023;
}
你可能会猜到Z和Y是一样的,但我们使用的数字是20而不是10。
int getZ()
{
return (position >> 20) & 1023;
}
char
不一定是 8 位。请参见 limits.h
中的 CHAR_BIT
。 - Michał Šrajerchar
是16位的 :) - fredoverflow