使用-1将所有位设置为true是否安全?

我已经看到这种模式在C&C ++中使用了很多。

unsigned int flags = -1; // all bits are true 

这是一个很好的便携式的方式来完成这个? 或者是使用0xffffffff0xffffffff更好?

我build议你完全按照你所说的去做,因为这是最直接的。 初始化为-1始终工作,独立于实际的符号表示,而~有时会有令人惊讶的行为,因为你将不得不有正确的操作数types。 只有这样你才能得到unsignedtypes的最高值。

举一个可能的惊喜的例子,考虑一下:

 unsigned long a = ~0u; 

它不一定将所有比特1的模式存储到a 。 但它将首先创build一个unsigned int所有位为1的模式,然后将其分配给a 。 当unsigned long有更多位时会发生什么,并不是所有的都是1。

并考虑这个,这将在一个非二的补码表示失败:

 unsigned int a = ~0; // Should have done ~0u ! 

原因是〜0必须反转所有位。 反过来,在二进制补码机器(这是我们需要的值!)上将产生-1 ,但在另一个表示上不会产生-1 。 在补码机上,它会产生零。 因此,在一台补码机上,上面将初始化为零。

你应该明白的是,这一切都是关于价值 – 而不是一点点。 variables用一个初始化。 如果在初始化器中修改用于初始化的variables的位,则会根据这些位生成值。 您需要将初始化为最高可能值的值为-1UINT_MAX 。 第二个将取决于types – 您将需要使用ULONG_MAX作为unsigned long 。 然而,第一个不会取决于它的types,这是获得最高价值的好方法。

我们不是在讨论-1是否全部是第一位(它并不总是)。 而且我们不是在讨论〜0是否全部是第一(当然是)。

但是我们所说的是初始化flagsvariables的结果。 而对于它, 只有-1会适用于所有types和机器。

  • unsigned int flags = -1; 是便携式的。
  • unsigned int flags = ~0; 是不可移植的,因为它依赖于二进制补码表示。
  • unsigned int flags = 0xffffffff; 不可移植,因为它假定32位整数。

如果你想以C标准保证的方式设置所有的位,使用第一个。

坦率地说,我认为所有的fff更具可读性。 至于它是一个反模式的评论,如果你真的关心所有的位被设置/清除,我会争辩说,你可能在你关心variables的大小的情况下,这将需要像boost :: uint16_t等

避免提到的问题的一个方法是简单地做:

 unsigned int flags = 0; flags = ~flags; 

便携式和重点。

我不确定在C ++中使用unsigned int标志是一个好主意。 那么bitset之类的呢?

std::numeric_limit<unsigned int>::max()更好,因为0xffffffff假定unsigned int是一个32位整数。

 unsigned int flags = -1; // all bits are true 

“这是一个很好的,便携的方式来完成这个吗?”

便携式? 是的

好? 可疑的 ,这个线程显示的所有困惑都certificate了这一点。 要清楚你的程序员能够理解代码而不会混淆,这应该是我们衡量好代码的一个维度。

另外,这种方法很容易出现编译器警告 。 为了避免危害编译器,你需要明确的强制转换。 例如,

 unsigned int flags = static_cast<unsigned int>(-1); 

明确的转换要求您注意目标types。 如果你注意目标types,那么你自然会避免其他方法的陷阱。

我的build议是注意目标types,并确保没有隐式转换。 例如:

 unsigned int flags1 = UINT_MAX; unsigned int flags2 = ~static_cast<unsigned int>(0); unsigned long flags3 = ULONG_MAX; unsigned long flags4 = ~static_cast<unsigned long>(0); 

所有这些对你的程序员来说都是正确和明显的

而在C ++ 11中 :我们可以使用auto来使这些更简单:

 auto flags1 = UINT_MAX; auto flags2 = ~static_cast<unsigned int>(0); auto flags3 = ULONG_MAX; auto flags4 = ~static_cast<unsigned long>(0); 

我认为正确和明显比简单正确。

将-1转换为任何无符号types由标准保证为全1。 使用~0U通常是不好的,因为0types是unsigned int并且不会填充更大的无符号types的所有位,除非明确地写入类似~0ULL东西。 在理智的系统中,〜0应该与-1相同,但是由于标准允许补码和符号/幅度表示,所以严格来说这不是可移植的。

当然,如果你知道你只需要32位就可以写出0xffffffff ,但是-1的好处是它可以在任何情况下工作,即使你不知道这个types的大小,例如在多个types,或者如果types的大小因实现而不同。 如果确实知道types,则另一种获得全部内容的安全方法是限制macrosUINT_MAXULONG_MAXULLONG_MAX等。

我个人总是用-1。 它总是有效的,你不必考虑它。

是。 正如其他答案中提到的, -1是最便携的; 但是,它不是很有意义,并触发编译器警告。

要解决这些问题,试试这个简单的帮手:

 static const struct All1s { template<typename UnsignedType> inline operator UnsignedType(void) const { return static_cast<UnsignedType>(-1); } } ALL_BITS_TRUE; 

用法:

 unsigned a = ALL_BITS_TRUE; uint8_t b = ALL_BITS_TRUE; uint16_t c = ALL_BITS_TRUE; uint32_t d = ALL_BITS_TRUE; uint64_t e = ALL_BITS_TRUE; 

只要你包含#include <limits.h> ,你就可以使用

 unsigned int flags = UINT_MAX; 

如果你想要一个很长的价值,你可以使用

 unsigned long flags = ULONG_MAX; 

这些值保证将结果集的所有值都设置为1,而不pipe签名整数如何实现。

我不会做-1的事情。 这是相当不直观的(至less对我来说)。 将签名数据分配给一个无符号variables似乎是违反了事物的自然顺序。

在你的情况下,我总是使用0xFFFF 。 (使用正确数量的Fs作为当然的可变尺寸。)

[顺便说一下,我很less看到在现实世界代码中完成的-1技巧。]

另外,如果你真的关心可放大的单个位,开始使用固定宽度的uint8_tuint16_tuint32_ttypes是个好主意。

在英特尔的IA-32处理器上写入0xFFFFFFFF到64位寄存器并得到预期的结果是可以的。 这是因为IA32e(IA32的64位扩展)仅支持32位立即数。 在64位指令中,32位立即数被符号扩展为64位。

以下是非法的:

 mov rax, 0ffffffffffffffffh 

以下是在RAX中放置64个1:

 mov rax, 0ffffffffh 

为了完整起见,下面在RAX(又名EAX)的下半部分放置32个1:

 mov eax, 0ffffffffh 

事实上,当我想将0xffffffff写入64位variables时,程序就会失败,取而代之的是0xffffffffffffffff。 在C这将是:

 uint64_t x; x = UINT64_C(0xffffffff) printf("x is %"PRIx64"\n", x); 

结果是:

 x is 0xffffffffffffffff 

我想这张贴作为评论,所有的答案,说0xFFFFFFFF假设32位,但有这么多人回答,我想我会添加它作为一个单独的答案。

请参阅litb的答案,对问题做出非常清晰的解释。

我的不同意是,严格来说,这两种情况都不能保证。 我并不知道任何体系结构并不代表所有位设置的'1位小于2的无符号值'的无符号值,但这是标准实际所说的(3.9.1 / 7加附注44):

整体types的表示应使用纯二进制数字系统来定义值。 [注释44:]使用二进制数字0和1的整数的位置表示,其中由连续位表示的值是加法的,以1开始,并且乘以2的连续整数幂,除了最高的位置。

这使得其中一个位的可能性是任何东西。

实际上:是的

理论上:不。

-1 = 0xFFFFFFFF(或者你的平台上int的大小)只有在二进制补码运算中才是正确的。 在实践中,它会起作用,但是那里有一些遗留的机器(IBM大型机等),在那里你有一个实际的符号位,而不是二进制补码表示。 你提出的〜0解决scheme应该到处工作。

尽pipe0xFFFF (或0xFFFFFFFF等)可能更容易阅读,但它可能破坏本来便携的代码的可移植性。 例如,考虑一个库例程来计算数据结构中有多less项具有特定的位(被调用者指定的确切位)。 程序可能完全不知道这些位代表什么,但是仍然需要“所有位设置”恒定。 在这种情况下,-1会比一个hex常数好得多,因为它可以处理任何位的大小。

另一种可能性是,如果typedef值用于位掩码,则将使用〜(bitMaskType)0; 如果掩码碰巧只是一个16位的types,那么这个expression式只会设置16位(即使“int”会是32位),但是由于16位将是所有需要的,实际上在types转换中使用了适当的types。

顺便说一下,如果hex常量太大而不能适应int ,但是会适合一个unsigned intlongvar &= ~[hex_constant]forms的expression式会有一个令人讨厌的问题。 如果int是16位,那么longvar &= ~0x4000;longvar &= ~0x10000 ; 将清除longvar ,但longvar &= ~0x8000; 将清除位15以及所有位。 符合int值会将补码运算符应用于inttypes,但结果将被标记为long ,并设置高位。 对于unsigned int来说太大的值会将complement运算符应用于inputlong 。 然而,在这些大小之间的值将应用补码运算符来键入unsigned int ,然后将其转换为无符号扩展的long unsigned int

正如其他人所提到的,-1是创build一个整数的正确方法,它将转换为所有位设置为1的无符号types。但是,C ++中最重要的是使用正确的types。 因此,你的问题的正确答案(包括你问的问题的答案)是这样的:

 std::bitset<32> const flags(-1); 

这将始终包含您需要的确切数量的位。 它构造了一个std::bitset ,其所有位都设置为1,这与其他答案中提到的相同。

这当然是安全的,因为-1总是有所有可用的位,但是我更喜欢〜0。 -1对于一个unsigned int没有多大意义。 0xFF …不好,因为它取决于types的宽度。

我说:

 int x; memset(&x, 0xFF, sizeof(int)); 

这总是会给你想要的结果。

利用将所有位分配给无符号types的事实相当于为给定types取最大可能值,
并将问题的范围扩展到所有无符号整数types:

指定-1对于 C和C ++都适用于任何无符号整数types (unsigned int,uint8_t,uint16_t等)。

作为替代scheme,对于C ++,您可以:

  1. 包括<limits>并使用std::numeric_limits< your_type >::max()
  2. 编写一个自定义模板函数 (这也将允许一些理智检查,即如果目标types是一个真正的无符号types)

目的可以增加更多的清晰度,因为分配-1总是需要一些解释性的评论。

是的,所显示的表示是非常正确的,就好像我们这样做了,反过来,你将需要一个操作符来反转所有的位,但是在这种情况下,如果我们考虑机器中整数的大小,这个逻辑非常简单

例如在大多数机器中,一个整数是2个字节= 16位最大值它可以容纳的是2 ^ 16-1 = 65535 2 ^ 16 = 65536

0%65536 = 0 -1%65536 = 65535其中1111 …………. 1,并且所有的位都被设置为1(如果我们考虑残留类mod 65536)直截了当。

我猜

没有,如果你考虑这个概念,它完全用于未签名的整数,它实际上是工作

只需检查下面的程序片段

int main(){

 unsigned int a=2; cout<<(unsigned int)pow(double(a),double(sizeof(a)*8)); unsigned int b=-1; cout<<"\n"<<b; getchar(); return 0; 

}

答案为b = 4294967295,其中4个字节的整数为-1%2 ^ 32

因此它对于无符号整数是完全有效的

如有任何不一致的情况报告