单字节布尔。 为什么?

在C ++中,为什么布尔需要一个字节来存储true或false,只有一个位就足够了,如0表示false,1表示true。 (为什么Java也需要一个字节?)

其次,使用以下几点更安全?

struct Bool { bool trueOrFalse : 1; }; 

第三,即使安全,上面的技术真的会有帮助吗? 既然我听说我们在那里节省空间,但编译器生成的代码访问它们的速度比生成访问基元的代码更大更慢。

为什么布尔需要一个字节来存储真或假只有一个位就够了

因为C ++中的每个对象都必须是可单独寻址的* (也就是说,必须能够有一个指向它的指针)。 你不能寻址一个单独的位(至less不是在传统的硬件上)。

它使用以下几个更安全?

这是“安全的”,但没有太多的成就。

上述领域的技术真的会帮助吗?

不,出于与上述相同的原因;)

但编译器生成的代码访问它们的速度比生成访问原语的代码更大更慢。

是的,这是真的。 在大多数平台上,这需要访问包含字节(或int或其他),然后执行位移和位掩码操作来访问相关位。


如果您真的关心内存使用情况,可以使用C ++中的std::bitset或Java中的BitSet ,它们会打包比特。


*除less数例外。

使用一个比特要慢得多,分配也要复杂得多。 在C / C ++中,没有办法得到一个位的地址,所以你不能做&trueOrFalse

Java有一个BitSet和EnumSet都使用位图。 如果你的数字很小,可能没有太大的区别。 例如,对象必须至less与字节alignment,并且在HotSpot中是8字节alignment的(在C ++中,一个new对象可以是8到16字节alignment的)这意味着保存一些可能不会节省任何空间。

至less在Java中,Bits不会更快,除非它们更好地适应caching。

 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由C标准保证至less保存8位,但也可以由更多字节组成。 确切的数字可以通过limits.h中定义的CHAR_BITmacros(C)或climits (C ++)来获得。 今天, CHAR_BIT == 8是最常见的,但你不能依赖它(见这里 )。 然而,在POSIX兼容的系统和Windows上保证是8。

虽然不可能减less单个标志的内存占用,但是当然也可以组合多个标志。 除了手动执行所有的位操作之外,还有一些select:

  • 如果你知道编译时的位数
    1. 位字段 (如在你的问题)。 但要小心,字段的sorting不能保证,这可能会导致可移植性问题。
    2. std::bitset
  • 如果只知道运行时的大小
    1. boost::dynamic_bitset
    2. 如果你必须处理大的比特向量,看看BitMagic库 。 它支持压缩并进行了严格的调整。

正如其他人已经指出的那样,节省一点点并不总是一个好主意。 可能的缺点是:

  1. 较less可读的代码
  2. 由于额外的提取代码而降低了执行速度。
  3. 出于同样的原因,代码量的增加可能会超过数据消耗的节省。
  4. multithreading程序中隐藏的同步问题。 例如,通过两个不同的线程翻转两个不同的位可能会导致竞争状态。 相比之下,两个线程总是可以修改原始types的两个不同对象(例如char )。

通常情况下,当处理大量数据时是有意义的,因为那样你就可以从内存和caching的压力中受益。

你为什么不把状态存储到一个字节? 实际上没有testing下面,但它应该给你一个想法。 你甚至可以使用16或32个状态的short或int。 我相信我也有一个工作的JAVA例子。 当我find它时,我会发布这个。

 __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值,因为。 如果我没有记错,即使使用一个字节反正会使用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(); } } 

另外我还要指出,你真的不应该为此使用一个特殊的类,而只是将最有可能利用它的类中存储的variables存储起来。 如果你打算有100或者甚至是1000的布尔值考虑一个字节数组。

例如下面的例子。

 boolean[] states = new boolean[4096]; 

可以转换成下面的。

 int[] states = new int[128]; 

现在你可能想知道如何从128个数组访问索引4095。 那么这是干什么的,如果我们简化它。 4095被右移5位,在技术上与32除相同。所以4095/32 =舍入(127)。 所以我们在数组的索引127。 然后我们执行4095&31,将其转换为0到31之间的一个值。这将只能使用2减1的幂。例如0,1,3,7,15,31,63,127,255,511,1023等等。

所以现在我们可以访问那个位置 正如你所看到的,这是非常非常紧凑的,并且在一个文件中有4096个布尔值的跳动:)这也将提供更快的读/写到二进制文件。 我不知道这个BitSet的东西是什么,但它看起来像完整的垃圾,因为从技术上说,字节,短,整型,长已经在他们的位forms,你也可以使用它们。 然后创build一些复杂的类来访问内存中的个别位,这是我可以从阅读几篇文章中得到的。

 boolean getState(int index) { return (states[index >> 5] & 1 << (index & 0x1F)) != 0x0; } 

更多信息…

基本上,如果上述有点混淆,这是一个简化的版本。

byte ”,“ short ”,“ int ”,“ long ”types都是具有不同范围的数据types。

您可以查看此链接: 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。

下面将validation一个单独的位是否为0或1. 0是错误的,1是正确的。

 bool getState(int bit) { return (state & (1 << bit)) != 0x0; } 

下面是检查两个值是否包含这些位的示例。 想想看,每当我们这样做的时候,每一位都代表2 ^ BIT

我会快速浏览一些运营商。 我们刚刚稍微解释了“&”运算符。 现在为“|” 运营商。

执行以下操作时

 int value = 31; value |= 16; value |= 16; value |= 16; value |= 16; 

这个值仍然是31.这是因为bit 4或者2 ^ 4 = 16已经打开或者设置为1.所以执行“|” 将该位打开时返回该值。 如果它已经打开,则不做任何更改。 我们利用“| =”将variables实际设置为返回值。

而不是 – >“value = value | 16;”。 我们只是做“价值| = 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。

上面是伟大的伟大的方式来检查,如果我们说,我们正在制作一个video游戏,并希望检查是否按下button,如果他们中的任何一个被按下。 我们可以用一次检查简单地检查每一个比特,而且比在每一个状态上执行布尔检查要高效多less倍。

现在让我们说,我们有布尔值总是颠倒。

通常你会做类似的事情

 bool state = false; state = !state; 

那么这可以通过使用“ ^ ”运算符来完成。

正如我们执行“1 << N”来select该位的整个值。 我们也可以做相反的事情。 所以就像我们展示了“| =”如何存储返回值一样,我们将使用“^ =”来完成。 那么,如果我们把它关掉了,那么这是做什么的。 如果closures,我们将其打开。

 void reverseState(int bit) { state ^= (1 << bit); } 

你甚至可以让它返回当前状态。 如果你想要它返回以前的状态只需交换“!=”到“==”。 所以这是做反转然后检查当前状态。

 bool reverseAndGet(int bit) { return ((state ^= (1 << bit)) & (1 << bit)) != 0x0; } 

将多个非单比特aka布尔值存储到一个int也可以完成。 比方说,我们通常写出如下的坐标位置。

 int posX = 0; int posY = 0; int posZ = 0; 

现在让我们假设这些从来没有通过1023.所以0到1023是所有这些的最大距离。 我为前面提到的其他目的select了1023,您可以操作“&”variables来强制0和2 ^ N – 1值之间的值。 所以我们假设你的范围是0到1023.我们可以执行“value&1023”,它总是一个0到1023之间的值,没有任何索引参数检查。 请记住,如前所述,这只适用于2减1的权力。 2 ^ 10 = 1024-1 = 1023。

例如,如果(value> = 0 && value <= 1023)没有更多。

所以2 ^ 10 = 1024,需要10位才能保存0到1023之间的数字。

所以10×3 = 30仍然小于或等于32.足以将所有这些值保存在一个int中。

所以我们可以执行以下操作。 所以看看我们用了多less位。 我们做0 + 10 + 20.我把0放在那里的原因是为了让你直观地看到2 ^ 0 = 1所以#* 1 =#。 我们需要y << 10的原因是因为x使用了从0到1023的10个比特。所以我们需要将y乘以1024来为每个取值唯一的值。 那么Z需要乘以2 ^ 20,即1,048,576。

 int position = (x << 0) | (y << 10) | (z << 20); 

这使得比较快速。

我们现在可以做

 return this.position == position; 

与…合作

 return this.x == x && this.y == y && this.z == z; 

现在如果我们想要每个人的实际位置呢?

对于x我们只需要做以下的事情。

 int getX() { return position & 1023; } 

那么对于我们来说,我们需要执行一个左移位,然后进行AND运算。

 int getY() { return (position >> 10) & 1023; } 

你可能猜测Z和Y是一样的,但是我们用20来代替10。

 int getZ() { return (position >> 20) & 1023; } 

我希望看到这个的人会发现它值得信息:)。

如果你真的想使用1位,你可以使用一个字符来存储8个布尔值,而移位则可以得到你想要的值。 我怀疑它会更快,而且这可能会给你带来很多令人头疼的问题,但从技术上讲这是可能的。

在一个侧面说明中,这样的尝试对于没有大量可用于variables的内存的系统certificate是有用的,但是具有更多的处理能力,那么您需要的是更多的处理能力。 我非常怀疑你会永远需要它。